Home > other >  How is Omit checked during compile time in TypeScript?
How is Omit checked during compile time in TypeScript?

Time:07-01

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.

  • Related