Aaron Aaron - 1 month ago 5
TypeScript Question

TypeScript Object.assign confusion

When I look at the signature of

I see that is uses intersection types, but the behavior is not what I expected.

I want to use
Object.assign
to merge some changes with an old object to create a new object (as is common practice in JS when following immutable data patterns) and have TypeScript validate that the parts and the result are correct, but it seems TypeScript allows anything through without an error.

For example:

interface Thing {
name: string;
age: number;
fav: boolean;
}

let oldThing: Thing = {
name: "Aaa",
age: 123,
fav: false
};

let newThing: Thing = Object.assign({}, oldThing, {
name: "Bbb",
age: "abc", // should be type error
fake: "fakey" // should be unknown prop error
});


Here it is in the playground.

Why does
Object.assign
allow this incorrect assignment to
Thing
? Is there a way to make this work close to what I expected?

Answer

The reason you intuitively expect an error here is that the intersection type constructed for the return value of Object.assign is absurd, and cannot be occupied by any real values. However, TypeScript has no way of verifying this. The return value from Object.assign here is typed as the following:

{
    name: string,
    age: string & number,
    fav: boolean,
    fake: string
}

Of course there is no actual value that is both string & number, but the compiler doesn't know that. It happily constructs this absurd type, then when you try to assign it to Thing verifies that the type of each property in Thing is satisfied by the corresponding property in the return value. Since this is the case (each of name, age and fav are present, and satisfy at least the required interface in Thing), the assignment succeeds.

If you allowed the type of newThing to be inferred, then tried assigning anything to the age property, you'd see the problem:

let newThing = Object.assign({}, oldThing, {
    name: "Bbb",
    age: "abc",
    fake: "fakey"
});
newThing.age = 10;   // Compiler error because 10 is not a string
newThing.age = "10"; // Compiler error because "10" is not a number
Comments