Skip to content

Fix #29168 #29174

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
39 changes: 38 additions & 1 deletion tests/baselines/reference/contextualTypeShouldBeLiteral.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -134,3 +161,13 @@ var xyz = {
}
};
xyz;
var testObj = {
items: {
hello: { type: 'string' },
world: {
items: {
nested: { type: 'string' }
}
}
}
};
56 changes: 56 additions & 0 deletions tests/baselines/reference/contextualTypeShouldBeLiteral.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
}
};

56 changes: 56 additions & 0 deletions tests/baselines/reference/contextualTypeShouldBeLiteral.types
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
};

Original file line number Diff line number Diff line change
Expand Up @@ -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; }'.
Expand Down Expand Up @@ -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 };
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/objectLiteralExcessProperties.types
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ function test<T extends IFoo>() {
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
Expand Down
28 changes: 27 additions & 1 deletion tests/cases/compiler/contextualTypeShouldBeLiteral.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,30 @@ 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' }
}
}
}
};