diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3be17bfdaa6da..7b9bcb2efbfd8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28417,6 +28417,96 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function checkObjectLiteral(node: ObjectLiteralExpression, checkMode?: CheckMode): Type { + const contextualType = getContextualType(node, /*contextFlags*/ undefined); + const isContextualTypeDependent = + node.parent.kind === SyntaxKind.CallExpression && + contextualType && + (contextualType.flags & TypeFlags.TypeParameter) && + contextualType.immediateBaseConstraint && + !!forEachChildRecursively( + (contextualType.symbol.declarations![0] as TypeParameterDeclaration).constraint!, + node => { + if ( + node.kind === SyntaxKind.TypeReference && + (node as TypeReferenceNode).typeName.kind === SyntaxKind.Identifier && + ((node as TypeReferenceNode).typeName as Identifier).escapedText === + (contextualType.symbol.declarations![0] as TypeParameterDeclaration).name.escapedText + ) { + return true; + } + } + ); + + if (!isContextualTypeDependent) { + return checkObjectLiteralNonDependently(node, checkMode); + } + + diagnostics.isStaging = true; + suggestionDiagnostics.isStaging = true; + let valueType = checkObjectLiteralNonDependently(node, checkMode); + let previousValueType = undefined as Type | undefined; + let passes = 0; + while(true) { + if (previousValueType && isTypeIdenticalTo(valueType, previousValueType)) { + commitDiagnostics(); + return valueType; + } + if (passes >= 3) { + commitDiagnostics(); + error(node, Diagnostics.Dependent_contextual_inference_requires_too_many_passes_and_possibly_infinite); + return valueType; + } + + const newContextualType = cloneTypeParameter(contextualType as TypeParameter); + newContextualType.immediateBaseConstraint = + instantiateType( + contextualType.immediateBaseConstraint, + createTypeMapper([contextualType], [valueType]) + ); + if (newContextualType.immediateBaseConstraint!.flags & TypeFlags.StructuredType) { + newContextualType.immediateBaseConstraint = resolveStructuredTypeMembers(newContextualType.immediateBaseConstraint as StructuredType); + } + + forEachChildRecursively(node, node => { + const nodeLinks = getNodeLinks(node); + nodeLinks.flags &= ~NodeCheckFlags.TypeChecked; + nodeLinks.flags &= ~NodeCheckFlags.ContextChecked; + nodeLinks.resolvedType = undefined; + nodeLinks.resolvedEnumType = undefined; + nodeLinks.resolvedSignature = undefined; + nodeLinks.resolvedSymbol = undefined; + nodeLinks.resolvedIndexInfo = undefined; + nodeLinks.contextFreeType = undefined; + + if (node.symbol) { + const symbolLinks = getSymbolLinks(node.symbol); + symbolLinks.type = undefined; + symbolLinks.typeParameters = undefined; + } + }); + + + node.contextualType = newContextualType; + revertDiagnostics(); + previousValueType = valueType; + valueType = checkObjectLiteralNonDependently(node); + passes++; + } + + function commitDiagnostics() { + diagnostics.commitStaged(); + suggestionDiagnostics.commitStaged(); + diagnostics.isStaging = false; + suggestionDiagnostics.isStaging = false; + } + + function revertDiagnostics() { + diagnostics.revertStaged(); + suggestionDiagnostics.revertStaged(); + } + } + + function checkObjectLiteralNonDependently(node: ObjectLiteralExpression, checkMode?: CheckMode): Type { const inDestructuringPattern = isAssignmentTarget(node); // Grammar checking checkGrammarObjectLiteralExpression(node, inDestructuringPattern); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index c0395bdfea0a4..9767ce5d46dc0 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -7392,6 +7392,10 @@ "category": "Message", "code": 95175 }, + "Dependent contextual inference requires too many passes and possibly infinite": { + "category": "Error", + "code": 95176 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d09d1f958de4c..eaa6603ed17ed 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -9135,6 +9135,10 @@ export interface DiagnosticCollection { // Otherwise, returns all the diagnostics (global and file associated) in this collection. getDiagnostics(): Diagnostic[]; getDiagnostics(fileName: string): DiagnosticWithLocation[]; + + isStaging: boolean; + commitStaged(): void; + revertStaged(): void; } // SyntaxKind.SyntaxList diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ade3c25fad373..c1306f03d27b6 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4397,11 +4397,19 @@ export function createDiagnosticCollection(): DiagnosticCollection { const fileDiagnostics = new Map>(); let hasReadNonFileDiagnostics = false; + let isStaging = false; + const stagingDiagnostics = [] as Diagnostic[]; + + return { add, lookup, getGlobalDiagnostics, getDiagnostics, + get isStaging() { return isStaging; }, + set isStaging(v) { isStaging = v; }, + commitStaged, + revertStaged, }; function lookup(diagnostic: Diagnostic): Diagnostic | undefined { @@ -4423,6 +4431,10 @@ export function createDiagnosticCollection(): DiagnosticCollection { } function add(diagnostic: Diagnostic): void { + if (isStaging) { + stagingDiagnostics.push(diagnostic); + return; + } let diagnostics: SortedArray | undefined; if (diagnostic.file) { diagnostics = fileDiagnostics.get(diagnostic.file.fileName); @@ -4464,6 +4476,19 @@ export function createDiagnosticCollection(): DiagnosticCollection { fileDiags.unshift(...nonFileDiagnostics); return fileDiags; } + + function commitStaged() { + const savedIsStaging = isStaging; + isStaging = false; + for (const diagnostic of stagingDiagnostics) { + add(diagnostic); + } + isStaging = savedIsStaging; + } + + function revertStaged() { + stagingDiagnostics.length = 0; + } } const templateSubstitutionRegExp = /\$\{/g; diff --git a/tests/baselines/reference/dependentContextualInference1.js b/tests/baselines/reference/dependentContextualInference1.js new file mode 100644 index 0000000000000..5e5fc8f4f00e1 --- /dev/null +++ b/tests/baselines/reference/dependentContextualInference1.js @@ -0,0 +1,20 @@ +//// [dependentContextualInference1.ts] +declare const f: + >(t: T) => T + +type F = + { a: unknown + , b: (a: T["a" & keyof T]) => unknown + } + +f({ + a: "hello", + b: x => x.toUpperCase() +}) + + +//// [dependentContextualInference1.js] +f({ + a: "hello", + b: function (x) { return x.toUpperCase(); } +}); diff --git a/tests/baselines/reference/dependentContextualInference1.symbols b/tests/baselines/reference/dependentContextualInference1.symbols new file mode 100644 index 0000000000000..f4825721971a5 --- /dev/null +++ b/tests/baselines/reference/dependentContextualInference1.symbols @@ -0,0 +1,41 @@ +=== tests/cases/compiler/dependentContextualInference1.ts === +declare const f: +>f : Symbol(f, Decl(dependentContextualInference1.ts, 0, 13)) + + >(t: T) => T +>T : Symbol(T, Decl(dependentContextualInference1.ts, 1, 3)) +>F : Symbol(F, Decl(dependentContextualInference1.ts, 1, 29)) +>T : Symbol(T, Decl(dependentContextualInference1.ts, 1, 3)) +>t : Symbol(t, Decl(dependentContextualInference1.ts, 1, 19)) +>T : Symbol(T, Decl(dependentContextualInference1.ts, 1, 3)) +>T : Symbol(T, Decl(dependentContextualInference1.ts, 1, 3)) + +type F = +>F : Symbol(F, Decl(dependentContextualInference1.ts, 1, 29)) +>T : Symbol(T, Decl(dependentContextualInference1.ts, 3, 7)) + + { a: unknown +>a : Symbol(a, Decl(dependentContextualInference1.ts, 4, 3)) + + , b: (a: T["a" & keyof T]) => unknown +>b : Symbol(b, Decl(dependentContextualInference1.ts, 5, 3)) +>a : Symbol(a, Decl(dependentContextualInference1.ts, 5, 8)) +>T : Symbol(T, Decl(dependentContextualInference1.ts, 3, 7)) +>T : Symbol(T, Decl(dependentContextualInference1.ts, 3, 7)) + } + +f({ +>f : Symbol(f, Decl(dependentContextualInference1.ts, 0, 13)) + + a: "hello", +>a : Symbol(a, Decl(dependentContextualInference1.ts, 8, 3)) + + b: x => x.toUpperCase() +>b : Symbol(b, Decl(dependentContextualInference1.ts, 9, 13)) +>x : Symbol(x, Decl(dependentContextualInference1.ts, 10, 4)) +>x.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(dependentContextualInference1.ts, 10, 4)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) + +}) + diff --git a/tests/baselines/reference/dependentContextualInference1.types b/tests/baselines/reference/dependentContextualInference1.types new file mode 100644 index 0000000000000..94e95f3595cf0 --- /dev/null +++ b/tests/baselines/reference/dependentContextualInference1.types @@ -0,0 +1,38 @@ +=== tests/cases/compiler/dependentContextualInference1.ts === +declare const f: +>f : >(t: T) => T + + >(t: T) => T +>t : T + +type F = +>F : F + + { a: unknown +>a : unknown + + , b: (a: T["a" & keyof T]) => unknown +>b : (a: T["a" & keyof T]) => unknown +>a : T["a" & keyof T] + } + +f({ +>f({ a: "hello", b: x => x.toUpperCase()}) : { a: string; b: (x: string) => string; } +>f : >(t: T) => T +>{ a: "hello", b: x => x.toUpperCase()} : { a: string; b: (x: string) => string; } + + a: "hello", +>a : string +>"hello" : "hello" + + b: x => x.toUpperCase() +>b : (x: string) => string +>x => x.toUpperCase() : (x: string) => string +>x : string +>x.toUpperCase() : string +>x.toUpperCase : () => string +>x : string +>toUpperCase : () => string + +}) + diff --git a/tests/baselines/reference/dependentContextualInference2.js b/tests/baselines/reference/dependentContextualInference2.js new file mode 100644 index 0000000000000..471a0cf2790dc --- /dev/null +++ b/tests/baselines/reference/dependentContextualInference2.js @@ -0,0 +1,26 @@ +//// [dependentContextualInference2.ts] +declare const m: >(m: T) => T +type M = + { a?: number + , b?: number + , c?: number + , d?: number + , k?: Exclude + , t?: (k: Exclude) => void + } + +m({ + a: 1, + b: 2, + k: "a", + t: k => {} +}) + + +//// [dependentContextualInference2.js] +m({ + a: 1, + b: 2, + k: "a", + t: function (k) { } +}); diff --git a/tests/baselines/reference/dependentContextualInference2.symbols b/tests/baselines/reference/dependentContextualInference2.symbols new file mode 100644 index 0000000000000..e55bcbcb0404d --- /dev/null +++ b/tests/baselines/reference/dependentContextualInference2.symbols @@ -0,0 +1,56 @@ +=== tests/cases/compiler/dependentContextualInference2.ts === +declare const m: >(m: T) => T +>m : Symbol(m, Decl(dependentContextualInference2.ts, 0, 13)) +>T : Symbol(T, Decl(dependentContextualInference2.ts, 0, 18)) +>M : Symbol(M, Decl(dependentContextualInference2.ts, 0, 44)) +>T : Symbol(T, Decl(dependentContextualInference2.ts, 0, 18)) +>m : Symbol(m, Decl(dependentContextualInference2.ts, 0, 34)) +>T : Symbol(T, Decl(dependentContextualInference2.ts, 0, 18)) +>T : Symbol(T, Decl(dependentContextualInference2.ts, 0, 18)) + +type M = +>M : Symbol(M, Decl(dependentContextualInference2.ts, 0, 44)) +>Self : Symbol(Self, Decl(dependentContextualInference2.ts, 1, 7)) + + { a?: number +>a : Symbol(a, Decl(dependentContextualInference2.ts, 2, 3)) + + , b?: number +>b : Symbol(b, Decl(dependentContextualInference2.ts, 3, 3)) + + , c?: number +>c : Symbol(c, Decl(dependentContextualInference2.ts, 4, 3)) + + , d?: number +>d : Symbol(d, Decl(dependentContextualInference2.ts, 5, 3)) + + , k?: Exclude +>k : Symbol(k, Decl(dependentContextualInference2.ts, 6, 3)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>Self : Symbol(Self, Decl(dependentContextualInference2.ts, 1, 7)) + + , t?: (k: Exclude) => void +>t : Symbol(t, Decl(dependentContextualInference2.ts, 7, 3)) +>k : Symbol(k, Decl(dependentContextualInference2.ts, 7, 9)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>Self : Symbol(Self, Decl(dependentContextualInference2.ts, 1, 7)) + } + +m({ +>m : Symbol(m, Decl(dependentContextualInference2.ts, 0, 13)) + + a: 1, +>a : Symbol(a, Decl(dependentContextualInference2.ts, 10, 3)) + + b: 2, +>b : Symbol(b, Decl(dependentContextualInference2.ts, 11, 7)) + + k: "a", +>k : Symbol(k, Decl(dependentContextualInference2.ts, 12, 7)) + + t: k => {} +>t : Symbol(t, Decl(dependentContextualInference2.ts, 13, 9)) +>k : Symbol(k, Decl(dependentContextualInference2.ts, 14, 4)) + +}) + diff --git a/tests/baselines/reference/dependentContextualInference2.types b/tests/baselines/reference/dependentContextualInference2.types new file mode 100644 index 0000000000000..b4876c4780d33 --- /dev/null +++ b/tests/baselines/reference/dependentContextualInference2.types @@ -0,0 +1,52 @@ +=== tests/cases/compiler/dependentContextualInference2.ts === +declare const m: >(m: T) => T +>m : >(m: T) => T +>m : T + +type M = +>M : M + + { a?: number +>a : number + + , b?: number +>b : number + + , c?: number +>c : number + + , d?: number +>d : number + + , k?: Exclude +>k : Exclude + + , t?: (k: Exclude) => void +>t : (k: Exclude) => void +>k : Exclude + } + +m({ +>m({ a: 1, b: 2, k: "a", t: k => {}}) : { a: number; b: number; k: "a"; t: (k: "a" | "b") => void; } +>m : >(m: T) => T +>{ a: 1, b: 2, k: "a", t: k => {}} : { a: number; b: number; k: "a"; t: (k: "a" | "b") => void; } + + a: 1, +>a : number +>1 : 1 + + b: 2, +>b : number +>2 : 2 + + k: "a", +>k : "a" +>"a" : "a" + + t: k => {} +>t : (k: "a" | "b") => void +>k => {} : (k: "a" | "b") => void +>k : "a" | "b" + +}) + diff --git a/tests/baselines/reference/dependentContextualInferenceMultiplePasses.js b/tests/baselines/reference/dependentContextualInferenceMultiplePasses.js new file mode 100644 index 0000000000000..f3ae94113e09e --- /dev/null +++ b/tests/baselines/reference/dependentContextualInferenceMultiplePasses.js @@ -0,0 +1,23 @@ +//// [dependentContextualInferenceMultiplePasses.ts] +declare const f: + >(t: T) => T + +type F = + { a: unknown + , b: (a: T["a" & keyof T]) => unknown + , c: (b: ReturnType unknown>>) => unknown + } + +f({ + a: ({ value: "a" as "a" }), + b: x => ({ value: x.value }), + c: x => ({ value: x.value }) +}) + + +//// [dependentContextualInferenceMultiplePasses.js] +f({ + a: ({ value: "a" }), + b: function (x) { return ({ value: x.value }); }, + c: function (x) { return ({ value: x.value }); } +}); diff --git a/tests/baselines/reference/dependentContextualInferenceMultiplePasses.symbols b/tests/baselines/reference/dependentContextualInferenceMultiplePasses.symbols new file mode 100644 index 0000000000000..63650c1c3ebc1 --- /dev/null +++ b/tests/baselines/reference/dependentContextualInferenceMultiplePasses.symbols @@ -0,0 +1,60 @@ +=== tests/cases/compiler/dependentContextualInferenceMultiplePasses.ts === +declare const f: +>f : Symbol(f, Decl(dependentContextualInferenceMultiplePasses.ts, 0, 13)) + + >(t: T) => T +>T : Symbol(T, Decl(dependentContextualInferenceMultiplePasses.ts, 1, 3)) +>F : Symbol(F, Decl(dependentContextualInferenceMultiplePasses.ts, 1, 29)) +>T : Symbol(T, Decl(dependentContextualInferenceMultiplePasses.ts, 1, 3)) +>t : Symbol(t, Decl(dependentContextualInferenceMultiplePasses.ts, 1, 19)) +>T : Symbol(T, Decl(dependentContextualInferenceMultiplePasses.ts, 1, 3)) +>T : Symbol(T, Decl(dependentContextualInferenceMultiplePasses.ts, 1, 3)) + +type F = +>F : Symbol(F, Decl(dependentContextualInferenceMultiplePasses.ts, 1, 29)) +>T : Symbol(T, Decl(dependentContextualInferenceMultiplePasses.ts, 3, 7)) + + { a: unknown +>a : Symbol(a, Decl(dependentContextualInferenceMultiplePasses.ts, 4, 3)) + + , b: (a: T["a" & keyof T]) => unknown +>b : Symbol(b, Decl(dependentContextualInferenceMultiplePasses.ts, 5, 3)) +>a : Symbol(a, Decl(dependentContextualInferenceMultiplePasses.ts, 5, 8)) +>T : Symbol(T, Decl(dependentContextualInferenceMultiplePasses.ts, 3, 7)) +>T : Symbol(T, Decl(dependentContextualInferenceMultiplePasses.ts, 3, 7)) + + , c: (b: ReturnType unknown>>) => unknown +>c : Symbol(c, Decl(dependentContextualInferenceMultiplePasses.ts, 6, 3)) +>b : Symbol(b, Decl(dependentContextualInferenceMultiplePasses.ts, 6, 8)) +>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(dependentContextualInferenceMultiplePasses.ts, 3, 7)) +>T : Symbol(T, Decl(dependentContextualInferenceMultiplePasses.ts, 3, 7)) +>a : Symbol(a, Decl(dependentContextualInferenceMultiplePasses.ts, 6, 49)) + } + +f({ +>f : Symbol(f, Decl(dependentContextualInferenceMultiplePasses.ts, 0, 13)) + + a: ({ value: "a" as "a" }), +>a : Symbol(a, Decl(dependentContextualInferenceMultiplePasses.ts, 9, 3)) +>value : Symbol(value, Decl(dependentContextualInferenceMultiplePasses.ts, 10, 7)) + + b: x => ({ value: x.value }), +>b : Symbol(b, Decl(dependentContextualInferenceMultiplePasses.ts, 10, 29)) +>x : Symbol(x, Decl(dependentContextualInferenceMultiplePasses.ts, 11, 4)) +>value : Symbol(value, Decl(dependentContextualInferenceMultiplePasses.ts, 11, 12)) +>x.value : Symbol(value, Decl(dependentContextualInferenceMultiplePasses.ts, 10, 7)) +>x : Symbol(x, Decl(dependentContextualInferenceMultiplePasses.ts, 11, 4)) +>value : Symbol(value, Decl(dependentContextualInferenceMultiplePasses.ts, 10, 7)) + + c: x => ({ value: x.value }) +>c : Symbol(c, Decl(dependentContextualInferenceMultiplePasses.ts, 11, 32)) +>x : Symbol(x, Decl(dependentContextualInferenceMultiplePasses.ts, 12, 4)) +>value : Symbol(value, Decl(dependentContextualInferenceMultiplePasses.ts, 12, 12)) +>x.value : Symbol(value, Decl(dependentContextualInferenceMultiplePasses.ts, 11, 12)) +>x : Symbol(x, Decl(dependentContextualInferenceMultiplePasses.ts, 12, 4)) +>value : Symbol(value, Decl(dependentContextualInferenceMultiplePasses.ts, 11, 12)) + +}) + diff --git a/tests/baselines/reference/dependentContextualInferenceMultiplePasses.types b/tests/baselines/reference/dependentContextualInferenceMultiplePasses.types new file mode 100644 index 0000000000000..75d33978609df --- /dev/null +++ b/tests/baselines/reference/dependentContextualInferenceMultiplePasses.types @@ -0,0 +1,60 @@ +=== tests/cases/compiler/dependentContextualInferenceMultiplePasses.ts === +declare const f: +>f : >(t: T) => T + + >(t: T) => T +>t : T + +type F = +>F : F + + { a: unknown +>a : unknown + + , b: (a: T["a" & keyof T]) => unknown +>b : (a: T["a" & keyof T]) => unknown +>a : T["a" & keyof T] + + , c: (b: ReturnType unknown>>) => unknown +>c : (b: ReturnType unknown>>) => unknown +>b : ReturnType unknown>> +>a : never[] + } + +f({ +>f({ a: ({ value: "a" as "a" }), b: x => ({ value: x.value }), c: x => ({ value: x.value })}) : { a: { value: "a"; }; b: (x: { value: "a"; }) => { value: "a"; }; c: (x: { value: "a"; }) => { value: "a"; }; } +>f : >(t: T) => T +>{ a: ({ value: "a" as "a" }), b: x => ({ value: x.value }), c: x => ({ value: x.value })} : { a: { value: "a"; }; b: (x: { value: "a"; }) => { value: "a"; }; c: (x: { value: "a"; }) => { value: "a"; }; } + + a: ({ value: "a" as "a" }), +>a : { value: "a"; } +>({ value: "a" as "a" }) : { value: "a"; } +>{ value: "a" as "a" } : { value: "a"; } +>value : "a" +>"a" as "a" : "a" +>"a" : "a" + + b: x => ({ value: x.value }), +>b : (x: { value: "a"; }) => { value: "a"; } +>x => ({ value: x.value }) : (x: { value: "a"; }) => { value: "a"; } +>x : { value: "a"; } +>({ value: x.value }) : { value: "a"; } +>{ value: x.value } : { value: "a"; } +>value : "a" +>x.value : "a" +>x : { value: "a"; } +>value : "a" + + c: x => ({ value: x.value }) +>c : (x: { value: "a"; }) => { value: "a"; } +>x => ({ value: x.value }) : (x: { value: "a"; }) => { value: "a"; } +>x : { value: "a"; } +>({ value: x.value }) : { value: "a"; } +>{ value: x.value } : { value: "a"; } +>value : "a" +>x.value : "a" +>x : { value: "a"; } +>value : "a" + +}) + diff --git a/tests/baselines/reference/dependentContextualInferenceTooManyPasses.errors.txt b/tests/baselines/reference/dependentContextualInferenceTooManyPasses.errors.txt new file mode 100644 index 0000000000000..163c9e6f629e7 --- /dev/null +++ b/tests/baselines/reference/dependentContextualInferenceTooManyPasses.errors.txt @@ -0,0 +1,31 @@ +tests/cases/compiler/dependentContextualInferenceTooManyPasses.ts(12,3): error TS95176: Dependent contextual inference requires too many passes and possibly infinite + + +==== tests/cases/compiler/dependentContextualInferenceTooManyPasses.ts (1 errors) ==== + declare const f: + >(t: T) => T + + type F = + { a: unknown + , b: (a: T["a" & keyof T]) => unknown + , c: (b: ReturnType unknown>>) => unknown + , d: (c: ReturnType unknown>>) => unknown + , e: (d: ReturnType unknown>>) => unknown + } + + f({ + ~ + a: ({ value: "a" as "a" }), + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + b: x => ({ value: x.value }), + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + c: x => ({ value: x.value }), + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + d: x => ({ value: x.value }), + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + e: x => ({ value: x.value }), + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + }) + ~ +!!! error TS95176: Dependent contextual inference requires too many passes and possibly infinite + \ No newline at end of file diff --git a/tests/baselines/reference/dependentContextualInferenceTooManyPasses.js b/tests/baselines/reference/dependentContextualInferenceTooManyPasses.js new file mode 100644 index 0000000000000..2dec878dc5097 --- /dev/null +++ b/tests/baselines/reference/dependentContextualInferenceTooManyPasses.js @@ -0,0 +1,29 @@ +//// [dependentContextualInferenceTooManyPasses.ts] +declare const f: + >(t: T) => T + +type F = + { a: unknown + , b: (a: T["a" & keyof T]) => unknown + , c: (b: ReturnType unknown>>) => unknown + , d: (c: ReturnType unknown>>) => unknown + , e: (d: ReturnType unknown>>) => unknown + } + +f({ + a: ({ value: "a" as "a" }), + b: x => ({ value: x.value }), + c: x => ({ value: x.value }), + d: x => ({ value: x.value }), + e: x => ({ value: x.value }), +}) + + +//// [dependentContextualInferenceTooManyPasses.js] +f({ + a: ({ value: "a" }), + b: function (x) { return ({ value: x.value }); }, + c: function (x) { return ({ value: x.value }); }, + d: function (x) { return ({ value: x.value }); }, + e: function (x) { return ({ value: x.value }); } +}); diff --git a/tests/baselines/reference/dependentContextualInferenceTooManyPasses.symbols b/tests/baselines/reference/dependentContextualInferenceTooManyPasses.symbols new file mode 100644 index 0000000000000..561dbfd1e0186 --- /dev/null +++ b/tests/baselines/reference/dependentContextualInferenceTooManyPasses.symbols @@ -0,0 +1,94 @@ +=== tests/cases/compiler/dependentContextualInferenceTooManyPasses.ts === +declare const f: +>f : Symbol(f, Decl(dependentContextualInferenceTooManyPasses.ts, 0, 13)) + + >(t: T) => T +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 1, 3)) +>F : Symbol(F, Decl(dependentContextualInferenceTooManyPasses.ts, 1, 29)) +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 1, 3)) +>t : Symbol(t, Decl(dependentContextualInferenceTooManyPasses.ts, 1, 19)) +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 1, 3)) +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 1, 3)) + +type F = +>F : Symbol(F, Decl(dependentContextualInferenceTooManyPasses.ts, 1, 29)) +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 3, 7)) + + { a: unknown +>a : Symbol(a, Decl(dependentContextualInferenceTooManyPasses.ts, 4, 3)) + + , b: (a: T["a" & keyof T]) => unknown +>b : Symbol(b, Decl(dependentContextualInferenceTooManyPasses.ts, 5, 3)) +>a : Symbol(a, Decl(dependentContextualInferenceTooManyPasses.ts, 5, 8)) +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 3, 7)) +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 3, 7)) + + , c: (b: ReturnType unknown>>) => unknown +>c : Symbol(c, Decl(dependentContextualInferenceTooManyPasses.ts, 6, 3)) +>b : Symbol(b, Decl(dependentContextualInferenceTooManyPasses.ts, 6, 8)) +>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 3, 7)) +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 3, 7)) +>a : Symbol(a, Decl(dependentContextualInferenceTooManyPasses.ts, 6, 49)) + + , d: (c: ReturnType unknown>>) => unknown +>d : Symbol(d, Decl(dependentContextualInferenceTooManyPasses.ts, 7, 3)) +>c : Symbol(c, Decl(dependentContextualInferenceTooManyPasses.ts, 7, 8)) +>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 3, 7)) +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 3, 7)) +>a : Symbol(a, Decl(dependentContextualInferenceTooManyPasses.ts, 7, 49)) + + , e: (d: ReturnType unknown>>) => unknown +>e : Symbol(e, Decl(dependentContextualInferenceTooManyPasses.ts, 8, 3)) +>d : Symbol(d, Decl(dependentContextualInferenceTooManyPasses.ts, 8, 8)) +>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 3, 7)) +>T : Symbol(T, Decl(dependentContextualInferenceTooManyPasses.ts, 3, 7)) +>a : Symbol(a, Decl(dependentContextualInferenceTooManyPasses.ts, 8, 49)) + } + +f({ +>f : Symbol(f, Decl(dependentContextualInferenceTooManyPasses.ts, 0, 13)) + + a: ({ value: "a" as "a" }), +>a : Symbol(a, Decl(dependentContextualInferenceTooManyPasses.ts, 11, 3)) +>value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 12, 7)) + + b: x => ({ value: x.value }), +>b : Symbol(b, Decl(dependentContextualInferenceTooManyPasses.ts, 12, 29)) +>x : Symbol(x, Decl(dependentContextualInferenceTooManyPasses.ts, 13, 4)) +>value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 13, 12)) +>x.value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 12, 7)) +>x : Symbol(x, Decl(dependentContextualInferenceTooManyPasses.ts, 13, 4)) +>value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 12, 7)) + + c: x => ({ value: x.value }), +>c : Symbol(c, Decl(dependentContextualInferenceTooManyPasses.ts, 13, 32)) +>x : Symbol(x, Decl(dependentContextualInferenceTooManyPasses.ts, 14, 4)) +>value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 14, 12)) +>x.value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 13, 12)) +>x : Symbol(x, Decl(dependentContextualInferenceTooManyPasses.ts, 14, 4)) +>value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 13, 12)) + + d: x => ({ value: x.value }), +>d : Symbol(d, Decl(dependentContextualInferenceTooManyPasses.ts, 14, 32)) +>x : Symbol(x, Decl(dependentContextualInferenceTooManyPasses.ts, 15, 4)) +>value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 15, 12)) +>x.value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 14, 12)) +>x : Symbol(x, Decl(dependentContextualInferenceTooManyPasses.ts, 15, 4)) +>value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 14, 12)) + + e: x => ({ value: x.value }), +>e : Symbol(e, Decl(dependentContextualInferenceTooManyPasses.ts, 15, 32)) +>x : Symbol(x, Decl(dependentContextualInferenceTooManyPasses.ts, 16, 4)) +>value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 16, 12)) +>x.value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 15, 12)) +>x : Symbol(x, Decl(dependentContextualInferenceTooManyPasses.ts, 16, 4)) +>value : Symbol(value, Decl(dependentContextualInferenceTooManyPasses.ts, 15, 12)) + +}) + diff --git a/tests/baselines/reference/dependentContextualInferenceTooManyPasses.types b/tests/baselines/reference/dependentContextualInferenceTooManyPasses.types new file mode 100644 index 0000000000000..3bdb7713a1514 --- /dev/null +++ b/tests/baselines/reference/dependentContextualInferenceTooManyPasses.types @@ -0,0 +1,92 @@ +=== tests/cases/compiler/dependentContextualInferenceTooManyPasses.ts === +declare const f: +>f : >(t: T) => T + + >(t: T) => T +>t : T + +type F = +>F : F + + { a: unknown +>a : unknown + + , b: (a: T["a" & keyof T]) => unknown +>b : (a: T["a" & keyof T]) => unknown +>a : T["a" & keyof T] + + , c: (b: ReturnType unknown>>) => unknown +>c : (b: ReturnType unknown>>) => unknown +>b : ReturnType unknown>> +>a : never[] + + , d: (c: ReturnType unknown>>) => unknown +>d : (c: ReturnType unknown>>) => unknown +>c : ReturnType unknown>> +>a : never[] + + , e: (d: ReturnType unknown>>) => unknown +>e : (d: ReturnType unknown>>) => unknown +>d : ReturnType unknown>> +>a : never[] + } + +f({ +>f({ a: ({ value: "a" as "a" }), b: x => ({ value: x.value }), c: x => ({ value: x.value }), d: x => ({ value: x.value }), e: x => ({ value: x.value }),}) : { a: { value: "a"; }; b: (x: { value: "a"; }) => { value: "a"; }; c: (x: { value: "a"; }) => { value: "a"; }; d: (x: { value: "a"; }) => { value: "a"; }; e: (x: { value: "a"; }) => { value: any; }; } +>f : >(t: T) => T +>{ a: ({ value: "a" as "a" }), b: x => ({ value: x.value }), c: x => ({ value: x.value }), d: x => ({ value: x.value }), e: x => ({ value: x.value }),} : { a: { value: "a"; }; b: (x: { value: "a"; }) => { value: "a"; }; c: (x: { value: "a"; }) => { value: "a"; }; d: (x: { value: "a"; }) => { value: "a"; }; e: (x: { value: "a"; }) => { value: "a"; }; } + + a: ({ value: "a" as "a" }), +>a : { value: "a"; } +>({ value: "a" as "a" }) : { value: "a"; } +>{ value: "a" as "a" } : { value: "a"; } +>value : "a" +>"a" as "a" : "a" +>"a" : "a" + + b: x => ({ value: x.value }), +>b : (x: { value: "a"; }) => { value: "a"; } +>x => ({ value: x.value }) : (x: { value: "a"; }) => { value: "a"; } +>x : { value: "a"; } +>({ value: x.value }) : { value: "a"; } +>{ value: x.value } : { value: "a"; } +>value : "a" +>x.value : "a" +>x : { value: "a"; } +>value : "a" + + c: x => ({ value: x.value }), +>c : (x: { value: "a"; }) => { value: "a"; } +>x => ({ value: x.value }) : (x: { value: "a"; }) => { value: "a"; } +>x : { value: "a"; } +>({ value: x.value }) : { value: "a"; } +>{ value: x.value } : { value: "a"; } +>value : "a" +>x.value : "a" +>x : { value: "a"; } +>value : "a" + + d: x => ({ value: x.value }), +>d : (x: { value: "a"; }) => { value: "a"; } +>x => ({ value: x.value }) : (x: { value: "a"; }) => { value: "a"; } +>x : { value: "a"; } +>({ value: x.value }) : { value: "a"; } +>{ value: x.value } : { value: "a"; } +>value : "a" +>x.value : "a" +>x : { value: "a"; } +>value : "a" + + e: x => ({ value: x.value }), +>e : (x: { value: "a"; }) => { value: "a"; } +>x => ({ value: x.value }) : (x: { value: "a"; }) => { value: "a"; } +>x : { value: "a"; } +>({ value: x.value }) : { value: "a"; } +>{ value: x.value } : { value: "a"; } +>value : "a" +>x.value : "a" +>x : { value: "a"; } +>value : "a" + +}) + diff --git a/tests/cases/compiler/dependentContextualInference1.ts b/tests/cases/compiler/dependentContextualInference1.ts new file mode 100644 index 0000000000000..8387c49acc513 --- /dev/null +++ b/tests/cases/compiler/dependentContextualInference1.ts @@ -0,0 +1,12 @@ +declare const f: + >(t: T) => T + +type F = + { a: unknown + , b: (a: T["a" & keyof T]) => unknown + } + +f({ + a: "hello", + b: x => x.toUpperCase() +}) diff --git a/tests/cases/compiler/dependentContextualInference2.ts b/tests/cases/compiler/dependentContextualInference2.ts new file mode 100644 index 0000000000000..f0d9b12db29f2 --- /dev/null +++ b/tests/cases/compiler/dependentContextualInference2.ts @@ -0,0 +1,16 @@ +declare const m: >(m: T) => T +type M = + { a?: number + , b?: number + , c?: number + , d?: number + , k?: Exclude + , t?: (k: Exclude) => void + } + +m({ + a: 1, + b: 2, + k: "a", + t: k => {} +}) diff --git a/tests/cases/compiler/dependentContextualInferenceMultiplePasses.ts b/tests/cases/compiler/dependentContextualInferenceMultiplePasses.ts new file mode 100644 index 0000000000000..ba0aa2ea101f8 --- /dev/null +++ b/tests/cases/compiler/dependentContextualInferenceMultiplePasses.ts @@ -0,0 +1,14 @@ +declare const f: + >(t: T) => T + +type F = + { a: unknown + , b: (a: T["a" & keyof T]) => unknown + , c: (b: ReturnType unknown>>) => unknown + } + +f({ + a: ({ value: "a" as "a" }), + b: x => ({ value: x.value }), + c: x => ({ value: x.value }) +}) diff --git a/tests/cases/compiler/dependentContextualInferenceTooManyPasses.ts b/tests/cases/compiler/dependentContextualInferenceTooManyPasses.ts new file mode 100644 index 0000000000000..25ddc85af1a12 --- /dev/null +++ b/tests/cases/compiler/dependentContextualInferenceTooManyPasses.ts @@ -0,0 +1,18 @@ +declare const f: + >(t: T) => T + +type F = + { a: unknown + , b: (a: T["a" & keyof T]) => unknown + , c: (b: ReturnType unknown>>) => unknown + , d: (c: ReturnType unknown>>) => unknown + , e: (d: ReturnType unknown>>) => unknown + } + +f({ + a: ({ value: "a" as "a" }), + b: x => ({ value: x.value }), + c: x => ({ value: x.value }), + d: x => ({ value: x.value }), + e: x => ({ value: x.value }), +})