In the following code, why does the personInvalidAndErrorCaught assignment give a TypeScript error (as I would expect) but the personInvalidButErrorUncaught does not? Especially since both lines transpile to the same JavaScript?
TypeScript:
interface Person {
name: string;
}
const person: Person = { name: "name" }
const personInvalidAndErrorCaught: Person = { ...person, invalidKey: "" } // Error as expected: Object literal may only specify known properties, and 'invalidKey' does not exist in type 'Person'.(2322)
const personInvalidButErrorUncaught: Person = { ...person, ...{ invalidKey: "" } } // Why no error? Especially since this line transpiles to the same JS as the line above?
Transpiled JavaScript:
"use strict";
const person = { name: "name" };
const personInvalidButCaught = Object.assign(Object.assign({}, person), { invalidKey: "" });
const personInvalidButUncaught = Object.assign(Object.assign({}, person), { invalidKey: "" });
Image showing error:

Playground Link
The difference is that you're directly assigning the object literal containing the invalid key in the case that doesn't work, and TypeScript treats that specially. From the beta documentation (they warn that link may rot):
Notice the given argument to
createSquareis spelledcolourinstead ofcolor. In plain JavaScript, this sort of thing fails silently.You could argue that this program is correctly typed, since the
widthproperties are compatible, there’s nocolorproperty present, and the extracolourproperty is insignificant.However, TypeScript takes the stance that there’s probably a bug in this code. Object literals get special treatment and undergo excess property checking when assigning them to other variables, or passing them as arguments.
(my emphasis)
(There's also the current documentation you found.)
In your second case where there's no error, you're not assigning the object literal with the excess property to the variable directly, you're assigning the wrapper object that it gets spread into. It's a subtle distinction, granted, but it's a distinction.
This is just a special case TypeScript flags up because when you're assigning the literal directly and specifying an invalid property, that's likely to be a bug. Something more complex may not be.
The fact that spreading doesn't trigger this check is, as you found in one of the tests, by design:
const a1: A = { ...extra1 }; // spread should not give excess property errors
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