diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 23c5568f2e5f4..c9d20bdf1955d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17531,7 +17531,7 @@ namespace ts { // be "pushed" onto a node using the contextualType property. function getApparentTypeOfContextualType(node: Expression): Type | undefined { let contextualType = getContextualType(node); - contextualType = contextualType && mapType(contextualType, getApparentType); + contextualType = contextualType && mapType(contextualType, getApparentType, /*noReductions*/ true); if (contextualType && contextualType.flags & TypeFlags.Union) { if (isObjectLiteralExpression(node)) { return discriminateContextualTypeByObjectMembers(node, contextualType as UnionType); diff --git a/tests/baselines/reference/contextualTypeShouldBeLiteral.js b/tests/baselines/reference/contextualTypeShouldBeLiteral.js index dcb9da993c1cb..77bd62d10f844 100644 --- a/tests/baselines/reference/contextualTypeShouldBeLiteral.js +++ b/tests/baselines/reference/contextualTypeShouldBeLiteral.js @@ -93,7 +93,34 @@ let xyz: LikeA | LikeB = { } }; -xyz; +xyz; + +// Repro: #29168 + +interface TestObject { + type?: 'object'; + items: { + [k: string]: TestGeneric; + }; +} + +interface TestString { + type: 'string'; +} + +type TestGeneric = (TestString | TestObject) & { [k: string]: any; }; + +const testObj: TestGeneric = { + items: { + hello: { type: 'string' }, + world: { + items: { + nested: { type: 'string' } + } + } + } +}; + //// [contextualTypeShouldBeLiteral.js] "use strict"; @@ -134,3 +161,13 @@ var xyz = { } }; xyz; +var testObj = { + items: { + hello: { type: 'string' }, + world: { + items: { + nested: { type: 'string' } + } + } + } +}; diff --git a/tests/baselines/reference/contextualTypeShouldBeLiteral.symbols b/tests/baselines/reference/contextualTypeShouldBeLiteral.symbols index 314117b509d94..156339e29b9a1 100644 --- a/tests/baselines/reference/contextualTypeShouldBeLiteral.symbols +++ b/tests/baselines/reference/contextualTypeShouldBeLiteral.symbols @@ -227,3 +227,59 @@ let xyz: LikeA | LikeB = { xyz; >xyz : Symbol(xyz, Decl(contextualTypeShouldBeLiteral.ts, 82, 3)) +// Repro: #29168 + +interface TestObject { +>TestObject : Symbol(TestObject, Decl(contextualTypeShouldBeLiteral.ts, 94, 4)) + + type?: 'object'; +>type : Symbol(TestObject.type, Decl(contextualTypeShouldBeLiteral.ts, 98, 22)) + + items: { +>items : Symbol(TestObject.items, Decl(contextualTypeShouldBeLiteral.ts, 99, 18)) + + [k: string]: TestGeneric; +>k : Symbol(k, Decl(contextualTypeShouldBeLiteral.ts, 101, 5)) +>TestGeneric : Symbol(TestGeneric, Decl(contextualTypeShouldBeLiteral.ts, 107, 1)) + + }; +} + +interface TestString { +>TestString : Symbol(TestString, Decl(contextualTypeShouldBeLiteral.ts, 103, 1)) + + type: 'string'; +>type : Symbol(TestString.type, Decl(contextualTypeShouldBeLiteral.ts, 105, 22)) +} + +type TestGeneric = (TestString | TestObject) & { [k: string]: any; }; +>TestGeneric : Symbol(TestGeneric, Decl(contextualTypeShouldBeLiteral.ts, 107, 1)) +>TestString : Symbol(TestString, Decl(contextualTypeShouldBeLiteral.ts, 103, 1)) +>TestObject : Symbol(TestObject, Decl(contextualTypeShouldBeLiteral.ts, 94, 4)) +>k : Symbol(k, Decl(contextualTypeShouldBeLiteral.ts, 109, 50)) + +const testObj: TestGeneric = { +>testObj : Symbol(testObj, Decl(contextualTypeShouldBeLiteral.ts, 111, 5)) +>TestGeneric : Symbol(TestGeneric, Decl(contextualTypeShouldBeLiteral.ts, 107, 1)) + + items: { +>items : Symbol(items, Decl(contextualTypeShouldBeLiteral.ts, 111, 30)) + + hello: { type: 'string' }, +>hello : Symbol(hello, Decl(contextualTypeShouldBeLiteral.ts, 112, 10)) +>type : Symbol(type, Decl(contextualTypeShouldBeLiteral.ts, 113, 12)) + + world: { +>world : Symbol(world, Decl(contextualTypeShouldBeLiteral.ts, 113, 30)) + + items: { +>items : Symbol(items, Decl(contextualTypeShouldBeLiteral.ts, 114, 12)) + + nested: { type: 'string' } +>nested : Symbol(nested, Decl(contextualTypeShouldBeLiteral.ts, 115, 14)) +>type : Symbol(type, Decl(contextualTypeShouldBeLiteral.ts, 116, 17)) + } + } + } +}; + diff --git a/tests/baselines/reference/contextualTypeShouldBeLiteral.types b/tests/baselines/reference/contextualTypeShouldBeLiteral.types index a5867f0de42dc..a3814f499033b 100644 --- a/tests/baselines/reference/contextualTypeShouldBeLiteral.types +++ b/tests/baselines/reference/contextualTypeShouldBeLiteral.types @@ -222,3 +222,59 @@ let xyz: LikeA | LikeB = { xyz; >xyz : LikeA +// Repro: #29168 + +interface TestObject { + type?: 'object'; +>type : "object" | undefined + + items: { +>items : { [k: string]: TestGeneric; } + + [k: string]: TestGeneric; +>k : string + + }; +} + +interface TestString { + type: 'string'; +>type : "string" +} + +type TestGeneric = (TestString | TestObject) & { [k: string]: any; }; +>TestGeneric : TestGeneric +>k : string + +const testObj: TestGeneric = { +>testObj : TestGeneric +>{ items: { hello: { type: 'string' }, world: { items: { nested: { type: 'string' } } } }} : { items: { hello: { type: "string"; }; world: { items: { nested: { type: "string"; }; }; }; }; } + + items: { +>items : { hello: { type: "string"; }; world: { items: { nested: { type: "string"; }; }; }; } +>{ hello: { type: 'string' }, world: { items: { nested: { type: 'string' } } } } : { hello: { type: "string"; }; world: { items: { nested: { type: "string"; }; }; }; } + + hello: { type: 'string' }, +>hello : { type: "string"; } +>{ type: 'string' } : { type: "string"; } +>type : "string" +>'string' : "string" + + world: { +>world : { items: { nested: { type: "string"; }; }; } +>{ items: { nested: { type: 'string' } } } : { items: { nested: { type: "string"; }; }; } + + items: { +>items : { nested: { type: "string"; }; } +>{ nested: { type: 'string' } } : { nested: { type: "string"; }; } + + nested: { type: 'string' } +>nested : { type: "string"; } +>{ type: 'string' } : { type: "string"; } +>type : "string" +>'string' : "string" + } + } + } +}; + diff --git a/tests/baselines/reference/objectLiteralExcessProperties.errors.txt b/tests/baselines/reference/objectLiteralExcessProperties.errors.txt index 4b034b06206df..e4fc4d69fca99 100644 --- a/tests/baselines/reference/objectLiteralExcessProperties.errors.txt +++ b/tests/baselines/reference/objectLiteralExcessProperties.errors.txt @@ -25,7 +25,7 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(37,25): error TS2304: Cann tests/cases/compiler/objectLiteralExcessProperties.ts(39,11): error TS2322: Type '{ name: string; }' is not assignable to type 'T'. tests/cases/compiler/objectLiteralExcessProperties.ts(41,11): error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type 'T & { prop: boolean; }'. Type '{ name: string; prop: boolean; }' is not assignable to type 'T'. -tests/cases/compiler/objectLiteralExcessProperties.ts(43,43): error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type 'T | { prop: boolean; }'. +tests/cases/compiler/objectLiteralExcessProperties.ts(43,43): error TS2322: Type '{ name: string; prop: true; }' is not assignable to type 'T | { prop: boolean; }'. Object literal may only specify known properties, and 'name' does not exist in type '{ prop: boolean; }'. tests/cases/compiler/objectLiteralExcessProperties.ts(45,76): error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type '{ name: string; } | (T & { prop: boolean; })'. Object literal may only specify known properties, and 'prop' does not exist in type '{ name: string; }'. @@ -119,7 +119,7 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(49,44): error TS2322: Type // Excess property checks only on non-generic parts of unions const obj3: T | { prop: boolean } = { name: "test", prop: true }; ~~~~~~~~~~~~ -!!! error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type 'T | { prop: boolean; }'. +!!! error TS2322: Type '{ name: string; prop: true; }' is not assignable to type 'T | { prop: boolean; }'. !!! error TS2322: Object literal may only specify known properties, and 'name' does not exist in type '{ prop: boolean; }'. // Excess property checks only on non-generic parts of unions const obj4: T & { prop: boolean } | { name: string } = { name: "test", prop: true }; diff --git a/tests/baselines/reference/objectLiteralExcessProperties.types b/tests/baselines/reference/objectLiteralExcessProperties.types index 8765432ab7040..812f48c89048c 100644 --- a/tests/baselines/reference/objectLiteralExcessProperties.types +++ b/tests/baselines/reference/objectLiteralExcessProperties.types @@ -124,10 +124,10 @@ function test() { const obj3: T | { prop: boolean } = { name: "test", prop: true }; >obj3 : T | { prop: boolean; } >prop : boolean ->{ name: "test", prop: true } : { name: string; prop: boolean; } +>{ name: "test", prop: true } : { name: string; prop: true; } >name : string >"test" : "test" ->prop : boolean +>prop : true >true : true // Excess property checks only on non-generic parts of unions diff --git a/tests/cases/compiler/contextualTypeShouldBeLiteral.ts b/tests/cases/compiler/contextualTypeShouldBeLiteral.ts index 1012c567a71b5..a73ab5143d894 100644 --- a/tests/cases/compiler/contextualTypeShouldBeLiteral.ts +++ b/tests/cases/compiler/contextualTypeShouldBeLiteral.ts @@ -93,4 +93,30 @@ let xyz: LikeA | LikeB = { } }; -xyz; \ No newline at end of file +xyz; + +// Repro: #29168 + +interface TestObject { + type?: 'object'; + items: { + [k: string]: TestGeneric; + }; +} + +interface TestString { + type: 'string'; +} + +type TestGeneric = (TestString | TestObject) & { [k: string]: any; }; + +const testObj: TestGeneric = { + items: { + hello: { type: 'string' }, + world: { + items: { + nested: { type: 'string' } + } + } + } +};