diff --git a/tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts b/tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts new file mode 100644 index 00000000000..1166e1c6ed3 --- /dev/null +++ b/tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts @@ -0,0 +1,52 @@ +// @strict: true +// @declaration: true + +// Object literals in unions are normalized upon widening +let a1 = [{ a: 0 }, { a: 1, b: "x" }, { a: 2, b: "y", c: true }][0]; +a1.a; // number +a1.b; // string | undefined +a1.c; // boolean | undefined +a1 = { a: 1 }; +a1 = { a: 0, b: 0 }; // Error +a1 = { b: "y" }; // Error +a1 = { c: true }; // Error + +let a2 = [{ a: 1, b: 2 }, { a: "abc" }, {}][0]; +a2.a; // string | number | undefined +a2.b; // number | undefined +a2 = { a: 10, b: 20 }; +a2 = { a: "def" }; +a2 = {}; +a2 = { a: "def", b: 20 }; // Error +a2 = { a: 1 }; // Error + +// Object literals containing spreads are not normalized +declare let b1: { a: string, b: string } | { b: string, c: string }; +let b2 = { ...b1, z: 55 }; +let b3 = { ...b2 }; + +// Before widening {} acts like { [x: string]: undefined }, which is a +// subtype of types with all optional properties +declare let opts: { foo?: string, bar?: string, baz?: boolean }; +let c1 = !true ? {} : opts; +let c2 = !true ? opts : {}; +let c3 = !true ? { a: 0, b: 0 } : {}; +let c4 = !true ? {} : { a: 0, b: 0 }; + +// Normalization applies to nested properties +let d1 = [{ kind: 'a', pos: { x: 0, y: 0 } }, { kind: 'b', pos: !true ? { a: "x" } : { b: 0 } }][0]; +d1.kind; +d1.pos; +d1.pos.x; +d1.pos.y; +d1.pos.a; +d1.pos.b; + +declare function f(...items: T[]): T; +declare let data: { a: 1, b: "abc", c: true }; + +// Object literals are inferred as a single normalized union type +let e1 = f({ a: 1, b: 2 }, { a: "abc" }, {}); +let e2 = f({}, { a: "abc" }, { a: 1, b: 2 }); +let e3 = f(data, { a: 2 }); +let e4 = f({ a: 2 }, data);