I'm failing to understand why this piece of code works when using the Omit utility type:
type Foo = {
prop1: string;
prop2: string;
}
const foo: Foo = {
prop1: 'prop1',
prop2: 'prop2'
}
console.log(foo) // {"prop1": "prop1", "prop2": "prop2"}
type Bar = Omit<Foo, 'prop2'> & {
prop3: string;
}
const bar: Bar = {
...foo,
prop3: 'prop3'
}
console.log(bar) // {"prop1": "prop1", "prop2": "prop2", "prop3": "prop3"}
Meanwhile, using Bar
directly fails as expected:
const qux: Bar = {
prop1: "prop1",
prop2: "prop2", // '{ prop1: string; prop2: string; prop3: string; }' is not assignable to type 'Bar'
prop3: "prop3"
}
CodePudding user response:
This happens because excess property checking only happens on properties in object literals, and only in some circumstances. Excess properties are not really a type safety issue; the compiler only warns about them if it thinks the code is likely to immediately forget about them. Something like const x: {a: string} = {a: "", b: ""}
is a problem because nothing will be able to access that b
property safely. But const y = {a: "", b: ""}; const x: {a: string} = y;
is fine because y
remembers b
even if x
forgets it.
Again, in general, extra properties are not a type safety issue. You want the compiler to allow extra properties, otherwise interface extension wouldn't work (if interface Foo {a: string}
and interface Bar extends Foo {b: string}
, then forbidding extra properties would mean that Bar
is not compatible with Foo
, and TypeScript's structural type system would be defeated). TypeScript doesn't really have "exact types" the way Flow does. There's an open request at microsoft/TypeScript#12936 to support exact types, but right now we don't have them. All we have is structural subtyping where {a: string, b: string}
is assignable to {a: string}
, and excess property checking which sometimes issues a warning about that, in very specific situations.
In the initializer to bar
you are spreading foo
, an already-existing variable. It's not an object literal, and therefore there is no excess property checking. This is working as intended as per ms/TS#41237.
On the other hand, your initializer for qux
is adding excess properties directly in an object literal, which does issue a warning, because nothing will remember that qux
has a prop2
property.