问题
I want to Object.assign
to an object of a known type, a set of properties from an object literal that should be of the same type. Is there a cleaner way to do this in TypeScript, without actually calling an identity function, or creating a separate variable, as suggested in this related question?
type Person = {
first?: string;
last?: string;
}
// Overkill, actually generates code to call a function
function check<T>(value: T): T { return value; }
const dude: Person = {
first: 'Mike',
};
Object.assign(dude, check<Person>({ // <- trying not to call a function
last: 'Johnson',
age: 27, // <-- should be flagged as unknown property
}));
TL;DR - looking for a way to type check an object literal directly.
回答1:
I can't think of a "perfect" solution to this that works in all circumstances. Most of the mechanisms that convince the compiler to perform the type checking you want are also accompanied by some added runtime code (such as a new intermediate variable assignment, or calling an identity function), which you've said you don't want.
One problem is that TypeScript doesn't have inline type annotations. (See microsoft/TypeScript#7481 and microsoft/TypeScript#13208 for discussion.) You'd like to be able to ask the compiler to verify that an expression {...}
is of type Person
, and have the compiler complain if it cannot be verified. The closest operator we have in TypeScript is a type assertion of the form {...} as Person
. But this tells the compiler that the expression {...}
is of the type Person
; you want to ask.
Even if we had an inline annotation operator, there's another problem: object types in TypeScript are not exact. (See microsoft/TypeScript#12936 for discussion.) Object types in TypeScript are open in that you can add more properties to them without breaking compatibility. If you have an object of type Person
, you know something about its first
and last
properties, but you really don't know anything about any other properties. Just because the definition of Person
doesn't mention an age
property, it does not mean that an object of type Person
cannot have an age
property. There could well be an interface like this:
interface AgedPerson extends Person {
age: number;
}
The structural nature of the TypeScript type system means that {last: "Johnson", age: 27}
is a valid AgedPerson
even if you don't declare it as such (and even if AgedPerson
is not defined). And since AgedPerson
is a valid subtype of Person
, then {last: "Johnson", age: 27}
is a valid Person
also.
Now, when people use object literals like {last: "Johnson", age: 27}
they usually don't intend to add such extra properties, so TypeScript has a feature called excess property checking which treats object literals as if they were of exact types and complains if you add unknown properties. This feature is useful, but it's very easy to circumvent. So it's important to mention that if you refactor your code at all, the warning about age
being an excess property might disappear:
const ageDude = { last: 'Johnson', age: 27 };
const personDude: Person = ageDude; // no error
That being said, for the particular example you gave, the solution I'd recommend would be:
Object.assign<Person, Person>(dude, {
last: 'Johnson',
age: 27, // error
});
Here you are manually specifying the generic type parameters on the call to Object.assign
, where the first type parameter corresponds to the type of the first function argument, and the second type parameter corresponds to the type of the second function argument. You want the compiler to treat both of those as Person
, so you should write Object.assign<Person, Person>(...)
. And the expected excess property error appears.
Okay, hope that helps; good luck!
Playground link to code
回答2:
Why is dude a constant if you're going around it with Object.assign?
Usually, if you try to assign anything that is not a 'Person' to dude, you will get an error e.g.
type Person = {
first?: string;
last?: string;
}
let dude: Person = {
first: 'Mike',
};
dude = {
last: 'Johnson',
age: 27
} // Error: Not assignable to type Person
This will type-check the assignment and also show an error if you wanted to assign to a constant.
来源:https://stackoverflow.com/questions/61106889/simpler-way-to-check-literal-object-type-in-typescript