This is a bit of a quirk in how unions work in conjunction with excess property checks. {A:1, C: 3}
is actually compatible with {C: number}
except for excess property checks:
const o = {A:1, C: 3};
const ok: {C: number} = o; // No direct literal assignment, no excess property checks
const nok: {C: number} = { A:1, C: 3}; // Excess property checks kick in
Playground Link
And the quirk of excess property checks is that for unions, it allows any property from any union constituent to be present in the assigned object literal.
You can get an error if the union constituents are incompatible one with another:
type T = {A: number, B: number} | {C: number, A?: undefined, B?: undefined };
const valid: T = {A: 1, B: 2};
const alsoValid: T = {C: 3};
// Error
const invalid: T = {A: 1, B: 2, C: 3};
//Error
const alsoInvalid: T = {A:1, C: 3};
Playground Link
You can also use the StrictUnion
from here if the union has a lot of memebers
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends any ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
type T = StrictUnion<{A: number, B: number} | {C: number }>;
const valid: T = {A: 1, B: 2};
const alsoValid: T = {C: 3};
// Error
const invalid: T = {A: 1, B: 2, C: 3};
//Error
const alsoInvalid: T = {A:1, C: 3};
Playground Link
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…