Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: An interface with optional properties is not assignable to a type with the same but required properties

Tags:

typescript

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?

like image 700
Need4Steed Avatar asked Jun 06 '16 09:06

Need4Steed


People also ask

What is an optional property in typescript?

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.

How to write interfaces with 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. If we don't use ? with property 'phone':

How to solve typescript error type 'X' is not assignable to type 'Y'?

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.

What is the difference between interfacewithoptional and interfacewithundefined in typescript?

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’.


1 Answers

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.


Edit

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)

like image 116
Nitzan Tomer Avatar answered Nov 06 '22 00:11

Nitzan Tomer