Here is what i wanna do:
const assign = <T extends U, U>(original: T, changes: U) =>
<T>Object.assign({}, original, changes);
interface IFoo {
bar: number,
baz?: string,
quux?: boolean,
}
let foo = <IFoo>{ bar: 42, baz: 'hello' };
assign(foo, { baz: 'world' }); //Compilation Error:
I just wanna implement a type safe assign
. but the compiler throws the error:
Argument of type 'IFoo' is not assignable to parameter of type '{ baz: string; }'. Property 'baz' is optional in type 'IFoo' but required in type '{ baz: string; }'.
The properties of changes
type should always be a strict subset of original
type, this kind of assignment should be totally logical, I think.
How do I get around it, appeasing the scrupulous compiler without sacrificing the type safety of the changes
parameter?
In TypeScript, the interfaces which describe objects can have optional properties. Interfaces with optional properties are written similar to other interfaces, with each optional property denoted by a ? at the end of the property name in the declaration.
Interfaces with optional properties are written similar to other interfaces, with each optional property denoted by a ? at the end of the property name in the declaration. If we don't use ? with property 'phone':
The "Type 'X' is not assignable to type 'Y'" TypeScript error occurs when the values on the left and right-hand side of the assignment have incompatible types. To solve the error, use a type assertion or a type guard to verify the two values have compatible types before the assignment. Here is a very simple example of how the error occurs.
They are subtly different, though, as evidenced by the fact that TypeScript won’t actually allow us to assign InterfaceWithOptional to InterfaceWithUndefined: Type ‘InterfaceWithOptional’ is not assignable to type ‘InterfaceWithUndefined’.
Your generics are wrong, if I understand you right.
It should be:
const assign = <T>(original: T, changes: T) => <T> Object.assign({}, original, changes);
interface IFoo {
bar?: number;
baz?: string;
quux?: boolean;
}
let foo = <IFoo> { bar: 42, baz: 'hello' };
assign(foo, { baz: 'world' });
(code in playground)
Both your original
and changes
are of the same type, just that each of them has different properties defined in the interface (based on your example of course).
You do not have two different types which one extend the other, so <T extends U, U>
is wrong.
Also, since you want to be able to only have subset of the properties in the interface then you must make them all optional, otherwise you'll get compilation errors.
Based on your comment, this might work for you:
const assign = <T extends U, U>(original: T, changes: U) => <T> Object.assign({}, original, changes);
interface FooOptional {
baz?: string;
quux?: boolean;
}
interface FooMandatory extends FooOptional {
bar: number;
}
let foo = { bar: 42, baz: 'hello' };
assign(foo, { baz: 'world' });
(code in playground)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With