diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c786d329c1bdc..fb7976bfed2cd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22384,6 +22384,10 @@ namespace ts { } } + function isCircularMappedProperty(symbol: Symbol) { + return !!(getCheckFlags(symbol) & CheckFlags.Mapped && !(symbol).type && findResolutionCycleStartIndex(symbol, TypeSystemPropertyName.Type) >= 0); + } + function getTypeOfPropertyOfContextualType(type: Type, name: __String) { return mapType(type, t => { if (isGenericMappedType(t)) { @@ -22397,7 +22401,7 @@ namespace ts { else if (t.flags & TypeFlags.StructuredType) { const prop = getPropertyOfType(t, name); if (prop) { - return getTypeOfSymbol(prop); + return isCircularMappedProperty(prop) ? undefined : getTypeOfSymbol(prop); } if (isTupleType(t)) { const restType = getRestTypeOfTupleType(t); diff --git a/tests/baselines/reference/circularContextualMappedType.js b/tests/baselines/reference/circularContextualMappedType.js new file mode 100644 index 0000000000000..7bd989c843a80 --- /dev/null +++ b/tests/baselines/reference/circularContextualMappedType.js @@ -0,0 +1,32 @@ +//// [circularContextualMappedType.ts] +type Func = () => T; + +type Mapped = { [K in keyof T]: Func }; + +declare function reproduce(options: number): void; +declare function reproduce(options: Mapped): T + +reproduce({ + name: () => { return 123 } +}); + +reproduce({ + name() { return 123 } +}); + +reproduce({ + name: function () { return 123 } +}); + + +//// [circularContextualMappedType.js] +"use strict"; +reproduce({ + name: function () { return 123; } +}); +reproduce({ + name: function () { return 123; } +}); +reproduce({ + name: function () { return 123; } +}); diff --git a/tests/baselines/reference/circularContextualMappedType.symbols b/tests/baselines/reference/circularContextualMappedType.symbols new file mode 100644 index 0000000000000..445906d48c1c7 --- /dev/null +++ b/tests/baselines/reference/circularContextualMappedType.symbols @@ -0,0 +1,51 @@ +=== tests/cases/compiler/circularContextualMappedType.ts === +type Func = () => T; +>Func : Symbol(Func, Decl(circularContextualMappedType.ts, 0, 0)) +>T : Symbol(T, Decl(circularContextualMappedType.ts, 0, 10)) +>T : Symbol(T, Decl(circularContextualMappedType.ts, 0, 10)) + +type Mapped = { [K in keyof T]: Func }; +>Mapped : Symbol(Mapped, Decl(circularContextualMappedType.ts, 0, 23)) +>T : Symbol(T, Decl(circularContextualMappedType.ts, 2, 12)) +>K : Symbol(K, Decl(circularContextualMappedType.ts, 2, 20)) +>T : Symbol(T, Decl(circularContextualMappedType.ts, 2, 12)) +>Func : Symbol(Func, Decl(circularContextualMappedType.ts, 0, 0)) +>T : Symbol(T, Decl(circularContextualMappedType.ts, 2, 12)) +>K : Symbol(K, Decl(circularContextualMappedType.ts, 2, 20)) + +declare function reproduce(options: number): void; +>reproduce : Symbol(reproduce, Decl(circularContextualMappedType.ts, 2, 48), Decl(circularContextualMappedType.ts, 4, 50)) +>options : Symbol(options, Decl(circularContextualMappedType.ts, 4, 27)) + +declare function reproduce(options: Mapped): T +>reproduce : Symbol(reproduce, Decl(circularContextualMappedType.ts, 2, 48), Decl(circularContextualMappedType.ts, 4, 50)) +>T : Symbol(T, Decl(circularContextualMappedType.ts, 5, 27)) +>options : Symbol(options, Decl(circularContextualMappedType.ts, 5, 30)) +>Mapped : Symbol(Mapped, Decl(circularContextualMappedType.ts, 0, 23)) +>T : Symbol(T, Decl(circularContextualMappedType.ts, 5, 27)) +>T : Symbol(T, Decl(circularContextualMappedType.ts, 5, 27)) + +reproduce({ +>reproduce : Symbol(reproduce, Decl(circularContextualMappedType.ts, 2, 48), Decl(circularContextualMappedType.ts, 4, 50)) + + name: () => { return 123 } +>name : Symbol(name, Decl(circularContextualMappedType.ts, 7, 11)) + +}); + +reproduce({ +>reproduce : Symbol(reproduce, Decl(circularContextualMappedType.ts, 2, 48), Decl(circularContextualMappedType.ts, 4, 50)) + + name() { return 123 } +>name : Symbol(name, Decl(circularContextualMappedType.ts, 11, 11)) + +}); + +reproduce({ +>reproduce : Symbol(reproduce, Decl(circularContextualMappedType.ts, 2, 48), Decl(circularContextualMappedType.ts, 4, 50)) + + name: function () { return 123 } +>name : Symbol(name, Decl(circularContextualMappedType.ts, 15, 11)) + +}); + diff --git a/tests/baselines/reference/circularContextualMappedType.types b/tests/baselines/reference/circularContextualMappedType.types new file mode 100644 index 0000000000000..d9439215d280e --- /dev/null +++ b/tests/baselines/reference/circularContextualMappedType.types @@ -0,0 +1,50 @@ +=== tests/cases/compiler/circularContextualMappedType.ts === +type Func = () => T; +>Func : Func + +type Mapped = { [K in keyof T]: Func }; +>Mapped : Mapped + +declare function reproduce(options: number): void; +>reproduce : { (options: number): void; (options: Mapped): T; } +>options : number + +declare function reproduce(options: Mapped): T +>reproduce : { (options: number): void; (options: Mapped): T; } +>options : Mapped + +reproduce({ +>reproduce({ name: () => { return 123 }}) : { name: number; } +>reproduce : { (options: number): void; (options: Mapped): T; } +>{ name: () => { return 123 }} : { name: () => number; } + + name: () => { return 123 } +>name : () => number +>() => { return 123 } : () => number +>123 : 123 + +}); + +reproduce({ +>reproduce({ name() { return 123 }}) : { name: number; } +>reproduce : { (options: number): void; (options: Mapped): T; } +>{ name() { return 123 }} : { name(): number; } + + name() { return 123 } +>name : () => number +>123 : 123 + +}); + +reproduce({ +>reproduce({ name: function () { return 123 }}) : { name: number; } +>reproduce : { (options: number): void; (options: Mapped): T; } +>{ name: function () { return 123 }} : { name: () => number; } + + name: function () { return 123 } +>name : () => number +>function () { return 123 } : () => number +>123 : 123 + +}); + diff --git a/tests/cases/compiler/circularContextualMappedType.ts b/tests/cases/compiler/circularContextualMappedType.ts new file mode 100644 index 0000000000000..4c982cd6fca1f --- /dev/null +++ b/tests/cases/compiler/circularContextualMappedType.ts @@ -0,0 +1,20 @@ +// @strict: true + +type Func = () => T; + +type Mapped = { [K in keyof T]: Func }; + +declare function reproduce(options: number): void; +declare function reproduce(options: Mapped): T + +reproduce({ + name: () => { return 123 } +}); + +reproduce({ + name() { return 123 } +}); + +reproduce({ + name: function () { return 123 } +});