From e90edb33d9a194b4541cd1b4d075192a29b4fd00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 25 Apr 2023 21:43:29 +0200 Subject: [PATCH 1/6] Improve inference for context sensitive functions within reverse mapped types --- src/compiler/checker.ts | 49 +- ...ionInferencesReverseMappedTypes.errors.txt | 148 ++++++ ...aExpressionInferencesReverseMappedTypes.js | 277 +++++++++++ ...essionInferencesReverseMappedTypes.symbols | 356 ++++++++++++++ ...pressionInferencesReverseMappedTypes.types | 443 ++++++++++++++++++ ...appedTypeContextualTypesApplied.errors.txt | 38 ++ .../mappedTypeContextualTypesApplied.types | 16 +- ...InferenceWithCircularConstraint.errors.txt | 65 +++ ...pedInferenceWithCircularConstraint.symbols | 142 ++++++ ...appedInferenceWithCircularConstraint.types | 119 +++++ ...reverseMappedPartiallyInferableTypes.types | 4 +- ...seMappedInferenceWithCircularConstraint.ts | 60 +++ ...aExpressionInferencesReverseMappedTypes.ts | 129 +++++ 13 files changed, 1813 insertions(+), 33 deletions(-) create mode 100644 tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.errors.txt create mode 100644 tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.js create mode 100644 tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.symbols create mode 100644 tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.types create mode 100644 tests/baselines/reference/mappedTypeContextualTypesApplied.errors.txt create mode 100644 tests/baselines/reference/reverseMappedInferenceWithCircularConstraint.errors.txt create mode 100644 tests/baselines/reference/reverseMappedInferenceWithCircularConstraint.symbols create mode 100644 tests/baselines/reference/reverseMappedInferenceWithCircularConstraint.types create mode 100644 tests/cases/compiler/reverseMappedInferenceWithCircularConstraint.ts create mode 100644 tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 81cb730122da7..71f28a8d63e0d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -430,6 +430,7 @@ import { InternalSymbolName, IntersectionType, IntersectionTypeNode, + IntraExpressionInferenceSite, intrinsicTagNameToString, IntrinsicType, introducesArgumentsExoticObject, @@ -23898,7 +23899,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!inference.isFixed) { // Before we commit to a particular inference (and thus lock out any further inferences), // we infer from any intra-expression inference sites we have collected. - inferFromIntraExpressionSites(context); + if (context.intraExpressionInferenceSites) { + inferFromIntraExpressionSites(context.inferences, context.intraExpressionInferenceSites); + } clearCachedInferences(context.inferences); inference.isFixed = true; } @@ -23937,17 +23940,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // arrow function. This happens automatically when the arrow functions are discrete arguments (because we // infer from each argument before processing the next), but when the arrow functions are elements of an // object or array literal, we need to perform intra-expression inferences early. - function inferFromIntraExpressionSites(context: InferenceContext) { - if (context.intraExpressionInferenceSites) { - for (const { node, type } of context.intraExpressionInferenceSites) { - const contextualType = node.kind === SyntaxKind.MethodDeclaration ? - getContextualTypeForObjectLiteralMethod(node as MethodDeclaration, ContextFlags.NoConstraints) : - getContextualType(node, ContextFlags.NoConstraints); - if (contextualType) { - inferTypes(context.inferences, type, contextualType); - } + function inferFromIntraExpressionSites(inferences: InferenceInfo[], intraExpressionInferenceSites: IntraExpressionInferenceSite[]) { + for (const { node, type } of intraExpressionInferenceSites) { + const contextualType = node.kind === SyntaxKind.MethodDeclaration ? + getContextualTypeForObjectLiteralMethod(node as MethodDeclaration, ContextFlags.NoConstraints) : + getContextualType(node, ContextFlags.NoConstraints); + if (contextualType) { + inferTypes(inferences, type, contextualType); } - context.intraExpressionInferenceSites = undefined; } } @@ -24071,20 +24071,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return type; } - // We consider a type to be partially inferable if it isn't marked non-inferable or if it is - // an object literal type with at least one property of an inferable type. For example, an object - // literal { a: 123, b: x => true } is marked non-inferable because it contains a context sensitive - // arrow function, but is considered partially inferable because property 'a' has an inferable type. - function isPartiallyInferableType(type: Type): boolean { - return !(getObjectFlags(type) & ObjectFlags.NonInferrableType) || - isObjectLiteralType(type) && some(getPropertiesOfType(type), prop => isPartiallyInferableType(getTypeOfSymbol(prop))) || - isTupleType(type) && some(getTypeArguments(type), isPartiallyInferableType); - } - function createReverseMappedType(source: Type, target: MappedType, constraint: IndexType) { // We consider a source type reverse mappable if it has a string index signature or if - // it has one or more properties and is of a partially inferable type. - if (!(getIndexInfoOfType(source, stringType) || getPropertiesOfType(source).length !== 0 && isPartiallyInferableType(source))) { + // it has one or more properties + if (!getIndexInfoOfType(source, stringType) && !getPropertiesOfType(source).length) { return undefined; } // For arrays and tuples we infer new arrays and tuples where the reverse mapping has been @@ -24121,6 +24111,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const templateType = getTemplateTypeFromMappedType(target); const inference = createInferenceInfo(typeParameter); inferTypes([inference], sourceType, templateType); + const sourceValueDeclaration = sourceType.symbol?.valueDeclaration; + if (sourceValueDeclaration) { + const intraExpressionInferenceSites = getInferenceContext(sourceValueDeclaration)?.intraExpressionInferenceSites?.filter(site => isNodeDescendantOf(site.node, sourceValueDeclaration)); + if (intraExpressionInferenceSites?.length) { + const templateType = (getApparentTypeOfContextualType(sourceValueDeclaration.parent.parent as Expression, ContextFlags.NoConstraints) as MappedType).templateType; + if (templateType) { + Debug.assert(isExpressionNode(sourceValueDeclaration)); + pushContextualType(sourceValueDeclaration as any as Expression, templateType, /*isCache*/ false); + inferFromIntraExpressionSites([inference], intraExpressionInferenceSites); + popContextualType(); + } + } + } return getTypeFromInference(inference) || unknownType; } diff --git a/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.errors.txt b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.errors.txt new file mode 100644 index 0000000000000..6c7ac51da1b59 --- /dev/null +++ b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.errors.txt @@ -0,0 +1,148 @@ +tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts(67,21): error TS18046: 'x' is of type 'unknown'. +tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts(71,21): error TS18046: 'x' is of type 'unknown'. +tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts(80,21): error TS18046: 'x' is of type 'unknown'. +tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts(86,21): error TS18046: 'x' is of type 'unknown'. +tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts(95,21): error TS18046: 'x' is of type 'unknown'. +tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts(101,21): error TS18046: 'x' is of type 'unknown'. + + +==== tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts (6 errors) ==== + // repro cases based on https://github.com/microsoft/TypeScript/issues/53018 + + declare function f( + arg: { + [K in keyof T]: { + produce: (n: string) => T[K]; + consume: (x: T[K]) => void; + }; + } + ): T; + + const res1 = f({ + a: { + produce: (n) => n, + consume: (x) => x.toLowerCase(), + }, + b: { + produce: (n) => ({ v: n }), + consume: (x) => x.v.toLowerCase(), + }, + }); + + const res2 = f({ + a: { + produce: function () { + return "hello"; + }, + consume: (x) => x.toLowerCase(), + }, + b: { + produce: function () { + return { v: "hello" }; + }, + consume: (x) => x.v.toLowerCase(), + }, + }); + + const res3 = f({ + a: { + produce() { + return "hello"; + }, + consume: (x) => x.toLowerCase(), + }, + b: { + produce() { + return { v: "hello" }; + }, + consume: (x) => x.v.toLowerCase(), + }, + }); + + declare function f2( + arg: [ + ...{ + [K in keyof T]: { + produce: (n: string) => T[K]; + consume: (x: T[K]) => void; + }; + } + ] + ): T; + + const res4 = f2([ + { + produce: (n) => n, + consume: (x) => x.toLowerCase(), + ~ +!!! error TS18046: 'x' is of type 'unknown'. + }, + { + produce: (n) => ({ v: n }), + consume: (x) => x.v.toLowerCase(), + ~ +!!! error TS18046: 'x' is of type 'unknown'. + }, + ]); + + const res5 = f2([ + { + produce: function () { + return "hello"; + }, + consume: (x) => x.toLowerCase(), + ~ +!!! error TS18046: 'x' is of type 'unknown'. + }, + { + produce: function () { + return { v: "hello" }; + }, + consume: (x) => x.v.toLowerCase(), + ~ +!!! error TS18046: 'x' is of type 'unknown'. + }, + ]); + + const res6 = f2([ + { + produce() { + return "hello"; + }, + consume: (x) => x.toLowerCase(), + ~ +!!! error TS18046: 'x' is of type 'unknown'. + }, + { + produce() { + return { v: "hello" }; + }, + consume: (x) => x.v.toLowerCase(), + ~ +!!! error TS18046: 'x' is of type 'unknown'. + }, + ]); + + declare function f3( + arg: { + [K in keyof T]: { + other: number, + produce: (n: string) => T[K]; + consume: (x: T[K]) => void; + }; + } + ): T; + + const res7 = f3({ + a: { + other: 42, + produce: (n) => n, + consume: (x) => x.toLowerCase(), + }, + b: { + other: 100, + produce: (n) => ({ v: n }), + consume: (x) => x.v.toLowerCase(), + }, + }); + \ No newline at end of file diff --git a/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.js b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.js new file mode 100644 index 0000000000000..706f7d9b32b04 --- /dev/null +++ b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.js @@ -0,0 +1,277 @@ +//// [intraExpressionInferencesReverseMappedTypes.ts] +// repro cases based on https://github.com/microsoft/TypeScript/issues/53018 + +declare function f( + arg: { + [K in keyof T]: { + produce: (n: string) => T[K]; + consume: (x: T[K]) => void; + }; + } +): T; + +const res1 = f({ + a: { + produce: (n) => n, + consume: (x) => x.toLowerCase(), + }, + b: { + produce: (n) => ({ v: n }), + consume: (x) => x.v.toLowerCase(), + }, +}); + +const res2 = f({ + a: { + produce: function () { + return "hello"; + }, + consume: (x) => x.toLowerCase(), + }, + b: { + produce: function () { + return { v: "hello" }; + }, + consume: (x) => x.v.toLowerCase(), + }, +}); + +const res3 = f({ + a: { + produce() { + return "hello"; + }, + consume: (x) => x.toLowerCase(), + }, + b: { + produce() { + return { v: "hello" }; + }, + consume: (x) => x.v.toLowerCase(), + }, +}); + +declare function f2( + arg: [ + ...{ + [K in keyof T]: { + produce: (n: string) => T[K]; + consume: (x: T[K]) => void; + }; + } + ] +): T; + +const res4 = f2([ + { + produce: (n) => n, + consume: (x) => x.toLowerCase(), + }, + { + produce: (n) => ({ v: n }), + consume: (x) => x.v.toLowerCase(), + }, +]); + +const res5 = f2([ + { + produce: function () { + return "hello"; + }, + consume: (x) => x.toLowerCase(), + }, + { + produce: function () { + return { v: "hello" }; + }, + consume: (x) => x.v.toLowerCase(), + }, +]); + +const res6 = f2([ + { + produce() { + return "hello"; + }, + consume: (x) => x.toLowerCase(), + }, + { + produce() { + return { v: "hello" }; + }, + consume: (x) => x.v.toLowerCase(), + }, +]); + +declare function f3( + arg: { + [K in keyof T]: { + other: number, + produce: (n: string) => T[K]; + consume: (x: T[K]) => void; + }; + } +): T; + +const res7 = f3({ + a: { + other: 42, + produce: (n) => n, + consume: (x) => x.toLowerCase(), + }, + b: { + other: 100, + produce: (n) => ({ v: n }), + consume: (x) => x.v.toLowerCase(), + }, +}); + + +//// [intraExpressionInferencesReverseMappedTypes.js] +"use strict"; +// repro cases based on https://github.com/microsoft/TypeScript/issues/53018 +var res1 = f({ + a: { + produce: function (n) { return n; }, + consume: function (x) { return x.toLowerCase(); }, + }, + b: { + produce: function (n) { return ({ v: n }); }, + consume: function (x) { return x.v.toLowerCase(); }, + }, +}); +var res2 = f({ + a: { + produce: function () { + return "hello"; + }, + consume: function (x) { return x.toLowerCase(); }, + }, + b: { + produce: function () { + return { v: "hello" }; + }, + consume: function (x) { return x.v.toLowerCase(); }, + }, +}); +var res3 = f({ + a: { + produce: function () { + return "hello"; + }, + consume: function (x) { return x.toLowerCase(); }, + }, + b: { + produce: function () { + return { v: "hello" }; + }, + consume: function (x) { return x.v.toLowerCase(); }, + }, +}); +var res4 = f2([ + { + produce: function (n) { return n; }, + consume: function (x) { return x.toLowerCase(); }, + }, + { + produce: function (n) { return ({ v: n }); }, + consume: function (x) { return x.v.toLowerCase(); }, + }, +]); +var res5 = f2([ + { + produce: function () { + return "hello"; + }, + consume: function (x) { return x.toLowerCase(); }, + }, + { + produce: function () { + return { v: "hello" }; + }, + consume: function (x) { return x.v.toLowerCase(); }, + }, +]); +var res6 = f2([ + { + produce: function () { + return "hello"; + }, + consume: function (x) { return x.toLowerCase(); }, + }, + { + produce: function () { + return { v: "hello" }; + }, + consume: function (x) { return x.v.toLowerCase(); }, + }, +]); +var res7 = f3({ + a: { + other: 42, + produce: function (n) { return n; }, + consume: function (x) { return x.toLowerCase(); }, + }, + b: { + other: 100, + produce: function (n) { return ({ v: n }); }, + consume: function (x) { return x.v.toLowerCase(); }, + }, +}); + + +//// [intraExpressionInferencesReverseMappedTypes.d.ts] +declare function f(arg: { + [K in keyof T]: { + produce: (n: string) => T[K]; + consume: (x: T[K]) => void; + }; +}): T; +declare const res1: { + a: string; + b: { + v: string; + }; +}; +declare const res2: { + a: string; + b: { + v: string; + }; +}; +declare const res3: { + a: string; + b: { + v: string; + }; +}; +declare function f2(arg: [ + ...{ + [K in keyof T]: { + produce: (n: string) => T[K]; + consume: (x: T[K]) => void; + }; + } +]): T; +declare const res4: [string, { + v: string; +}]; +declare const res5: [string, { + v: string; +}]; +declare const res6: [string, { + v: string; +}]; +declare function f3(arg: { + [K in keyof T]: { + other: number; + produce: (n: string) => T[K]; + consume: (x: T[K]) => void; + }; +}): T; +declare const res7: { + a: string; + b: { + v: string; + }; +}; diff --git a/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.symbols b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.symbols new file mode 100644 index 0000000000000..2ad8292ae4c6d --- /dev/null +++ b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.symbols @@ -0,0 +1,356 @@ +=== tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts === +// repro cases based on https://github.com/microsoft/TypeScript/issues/53018 + +declare function f( +>f : Symbol(f, Decl(intraExpressionInferencesReverseMappedTypes.ts, 0, 0)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 2, 19)) + + arg: { +>arg : Symbol(arg, Decl(intraExpressionInferencesReverseMappedTypes.ts, 2, 22)) + + [K in keyof T]: { +>K : Symbol(K, Decl(intraExpressionInferencesReverseMappedTypes.ts, 4, 5)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 2, 19)) + + produce: (n: string) => T[K]; +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 4, 21)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 5, 16)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 2, 19)) +>K : Symbol(K, Decl(intraExpressionInferencesReverseMappedTypes.ts, 4, 5)) + + consume: (x: T[K]) => void; +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 5, 35)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 6, 16)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 2, 19)) +>K : Symbol(K, Decl(intraExpressionInferencesReverseMappedTypes.ts, 4, 5)) + + }; + } +): T; +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 2, 19)) + +const res1 = f({ +>res1 : Symbol(res1, Decl(intraExpressionInferencesReverseMappedTypes.ts, 11, 5)) +>f : Symbol(f, Decl(intraExpressionInferencesReverseMappedTypes.ts, 0, 0)) + + a: { +>a : Symbol(a, Decl(intraExpressionInferencesReverseMappedTypes.ts, 11, 16)) + + produce: (n) => n, +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 12, 6)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 13, 14)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 13, 14)) + + consume: (x) => x.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 13, 22)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 14, 14)) +>x.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 14, 14)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) + + }, + b: { +>b : Symbol(b, Decl(intraExpressionInferencesReverseMappedTypes.ts, 15, 4)) + + produce: (n) => ({ v: n }), +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 16, 6)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 17, 14)) +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 17, 22)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 17, 14)) + + consume: (x) => x.v.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 17, 31)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 18, 14)) +>x.v.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>x.v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 17, 22)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 18, 14)) +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 17, 22)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) + + }, +}); + +const res2 = f({ +>res2 : Symbol(res2, Decl(intraExpressionInferencesReverseMappedTypes.ts, 22, 5)) +>f : Symbol(f, Decl(intraExpressionInferencesReverseMappedTypes.ts, 0, 0)) + + a: { +>a : Symbol(a, Decl(intraExpressionInferencesReverseMappedTypes.ts, 22, 16)) + + produce: function () { +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 23, 6)) + + return "hello"; + }, + consume: (x) => x.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 26, 6)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 27, 14)) +>x.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 27, 14)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) + + }, + b: { +>b : Symbol(b, Decl(intraExpressionInferencesReverseMappedTypes.ts, 28, 4)) + + produce: function () { +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 29, 6)) + + return { v: "hello" }; +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 31, 14)) + + }, + consume: (x) => x.v.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 32, 6)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 33, 14)) +>x.v.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>x.v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 31, 14)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 33, 14)) +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 31, 14)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) + + }, +}); + +const res3 = f({ +>res3 : Symbol(res3, Decl(intraExpressionInferencesReverseMappedTypes.ts, 37, 5)) +>f : Symbol(f, Decl(intraExpressionInferencesReverseMappedTypes.ts, 0, 0)) + + a: { +>a : Symbol(a, Decl(intraExpressionInferencesReverseMappedTypes.ts, 37, 16)) + + produce() { +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 38, 6)) + + return "hello"; + }, + consume: (x) => x.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 41, 6)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 42, 14)) +>x.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 42, 14)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) + + }, + b: { +>b : Symbol(b, Decl(intraExpressionInferencesReverseMappedTypes.ts, 43, 4)) + + produce() { +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 44, 6)) + + return { v: "hello" }; +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 46, 14)) + + }, + consume: (x) => x.v.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 47, 6)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 48, 14)) +>x.v.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>x.v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 46, 14)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 48, 14)) +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 46, 14)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) + + }, +}); + +declare function f2( +>f2 : Symbol(f2, Decl(intraExpressionInferencesReverseMappedTypes.ts, 50, 3)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 52, 20)) + + arg: [ +>arg : Symbol(arg, Decl(intraExpressionInferencesReverseMappedTypes.ts, 52, 41)) + + ...{ + [K in keyof T]: { +>K : Symbol(K, Decl(intraExpressionInferencesReverseMappedTypes.ts, 55, 7)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 52, 20)) + + produce: (n: string) => T[K]; +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 55, 23)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 56, 18)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 52, 20)) +>K : Symbol(K, Decl(intraExpressionInferencesReverseMappedTypes.ts, 55, 7)) + + consume: (x: T[K]) => void; +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 56, 37)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 57, 18)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 52, 20)) +>K : Symbol(K, Decl(intraExpressionInferencesReverseMappedTypes.ts, 55, 7)) + + }; + } + ] +): T; +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 52, 20)) + +const res4 = f2([ +>res4 : Symbol(res4, Decl(intraExpressionInferencesReverseMappedTypes.ts, 63, 5)) +>f2 : Symbol(f2, Decl(intraExpressionInferencesReverseMappedTypes.ts, 50, 3)) + { + produce: (n) => n, +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 64, 3)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 65, 14)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 65, 14)) + + consume: (x) => x.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 65, 22)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 66, 14)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 66, 14)) + + }, + { + produce: (n) => ({ v: n }), +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 68, 3)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 69, 14)) +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 69, 22)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 69, 14)) + + consume: (x) => x.v.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 69, 31)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 70, 14)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 70, 14)) + + }, +]); + +const res5 = f2([ +>res5 : Symbol(res5, Decl(intraExpressionInferencesReverseMappedTypes.ts, 74, 5)) +>f2 : Symbol(f2, Decl(intraExpressionInferencesReverseMappedTypes.ts, 50, 3)) + { + produce: function () { +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 75, 3)) + + return "hello"; + }, + consume: (x) => x.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 78, 6)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 79, 14)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 79, 14)) + + }, + { + produce: function () { +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 81, 3)) + + return { v: "hello" }; +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 83, 14)) + + }, + consume: (x) => x.v.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 84, 6)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 85, 14)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 85, 14)) + + }, +]); + +const res6 = f2([ +>res6 : Symbol(res6, Decl(intraExpressionInferencesReverseMappedTypes.ts, 89, 5)) +>f2 : Symbol(f2, Decl(intraExpressionInferencesReverseMappedTypes.ts, 50, 3)) + { + produce() { +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 90, 3)) + + return "hello"; + }, + consume: (x) => x.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 93, 6)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 94, 14)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 94, 14)) + + }, + { + produce() { +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 96, 3)) + + return { v: "hello" }; +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 98, 14)) + + }, + consume: (x) => x.v.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 99, 6)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 100, 14)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 100, 14)) + + }, +]); + +declare function f3( +>f3 : Symbol(f3, Decl(intraExpressionInferencesReverseMappedTypes.ts, 102, 3)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 104, 20)) + + arg: { +>arg : Symbol(arg, Decl(intraExpressionInferencesReverseMappedTypes.ts, 104, 23)) + + [K in keyof T]: { +>K : Symbol(K, Decl(intraExpressionInferencesReverseMappedTypes.ts, 106, 5)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 104, 20)) + + other: number, +>other : Symbol(other, Decl(intraExpressionInferencesReverseMappedTypes.ts, 106, 21)) + + produce: (n: string) => T[K]; +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 107, 20)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 108, 16)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 104, 20)) +>K : Symbol(K, Decl(intraExpressionInferencesReverseMappedTypes.ts, 106, 5)) + + consume: (x: T[K]) => void; +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 108, 35)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 109, 16)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 104, 20)) +>K : Symbol(K, Decl(intraExpressionInferencesReverseMappedTypes.ts, 106, 5)) + + }; + } +): T; +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 104, 20)) + +const res7 = f3({ +>res7 : Symbol(res7, Decl(intraExpressionInferencesReverseMappedTypes.ts, 114, 5)) +>f3 : Symbol(f3, Decl(intraExpressionInferencesReverseMappedTypes.ts, 102, 3)) + + a: { +>a : Symbol(a, Decl(intraExpressionInferencesReverseMappedTypes.ts, 114, 17)) + + other: 42, +>other : Symbol(other, Decl(intraExpressionInferencesReverseMappedTypes.ts, 115, 6)) + + produce: (n) => n, +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 116, 14)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 117, 14)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 117, 14)) + + consume: (x) => x.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 117, 22)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 118, 14)) +>x.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 118, 14)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) + + }, + b: { +>b : Symbol(b, Decl(intraExpressionInferencesReverseMappedTypes.ts, 119, 4)) + + other: 100, +>other : Symbol(other, Decl(intraExpressionInferencesReverseMappedTypes.ts, 120, 6)) + + produce: (n) => ({ v: n }), +>produce : Symbol(produce, Decl(intraExpressionInferencesReverseMappedTypes.ts, 121, 15)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 122, 14)) +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 122, 22)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 122, 14)) + + consume: (x) => x.v.toLowerCase(), +>consume : Symbol(consume, Decl(intraExpressionInferencesReverseMappedTypes.ts, 122, 31)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 123, 14)) +>x.v.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>x.v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 122, 22)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 123, 14)) +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 122, 22)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) + + }, +}); + diff --git a/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.types b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.types new file mode 100644 index 0000000000000..868796fd81925 --- /dev/null +++ b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.types @@ -0,0 +1,443 @@ +=== tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts === +// repro cases based on https://github.com/microsoft/TypeScript/issues/53018 + +declare function f( +>f : (arg: { [K in keyof T]: { produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; }) => T + + arg: { +>arg : { [K in keyof T]: { produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; } + + [K in keyof T]: { + produce: (n: string) => T[K]; +>produce : (n: string) => T[K] +>n : string + + consume: (x: T[K]) => void; +>consume : (x: T[K]) => void +>x : T[K] + + }; + } +): T; + +const res1 = f({ +>res1 : { a: string; b: { v: string; }; } +>f({ a: { produce: (n) => n, consume: (x) => x.toLowerCase(), }, b: { produce: (n) => ({ v: n }), consume: (x) => x.v.toLowerCase(), },}) : { a: string; b: { v: string; }; } +>f : (arg: { [K in keyof T]: { produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; }) => T +>{ a: { produce: (n) => n, consume: (x) => x.toLowerCase(), }, b: { produce: (n) => ({ v: n }), consume: (x) => x.v.toLowerCase(), },} : { a: { produce: (n: string) => string; consume: (x: string) => string; }; b: { produce: (n: string) => { v: string; }; consume: (x: { v: string; }) => string; }; } + + a: { +>a : { produce: (n: string) => string; consume: (x: string) => string; } +>{ produce: (n) => n, consume: (x) => x.toLowerCase(), } : { produce: (n: string) => string; consume: (x: string) => string; } + + produce: (n) => n, +>produce : (n: string) => string +>(n) => n : (n: string) => string +>n : string +>n : string + + consume: (x) => x.toLowerCase(), +>consume : (x: string) => string +>(x) => x.toLowerCase() : (x: string) => string +>x : string +>x.toLowerCase() : string +>x.toLowerCase : () => string +>x : string +>toLowerCase : () => string + + }, + b: { +>b : { produce: (n: string) => { v: string; }; consume: (x: { v: string; }) => string; } +>{ produce: (n) => ({ v: n }), consume: (x) => x.v.toLowerCase(), } : { produce: (n: string) => { v: string; }; consume: (x: { v: string; }) => string; } + + produce: (n) => ({ v: n }), +>produce : (n: string) => { v: string; } +>(n) => ({ v: n }) : (n: string) => { v: string; } +>n : string +>({ v: n }) : { v: string; } +>{ v: n } : { v: string; } +>v : string +>n : string + + consume: (x) => x.v.toLowerCase(), +>consume : (x: { v: string; }) => string +>(x) => x.v.toLowerCase() : (x: { v: string; }) => string +>x : { v: string; } +>x.v.toLowerCase() : string +>x.v.toLowerCase : () => string +>x.v : string +>x : { v: string; } +>v : string +>toLowerCase : () => string + + }, +}); + +const res2 = f({ +>res2 : { a: string; b: { v: string; }; } +>f({ a: { produce: function () { return "hello"; }, consume: (x) => x.toLowerCase(), }, b: { produce: function () { return { v: "hello" }; }, consume: (x) => x.v.toLowerCase(), },}) : { a: string; b: { v: string; }; } +>f : (arg: { [K in keyof T]: { produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; }) => T +>{ a: { produce: function () { return "hello"; }, consume: (x) => x.toLowerCase(), }, b: { produce: function () { return { v: "hello" }; }, consume: (x) => x.v.toLowerCase(), },} : { a: { produce: () => string; consume: (x: string) => string; }; b: { produce: () => { v: string; }; consume: (x: { v: string; }) => string; }; } + + a: { +>a : { produce: () => string; consume: (x: string) => string; } +>{ produce: function () { return "hello"; }, consume: (x) => x.toLowerCase(), } : { produce: () => string; consume: (x: string) => string; } + + produce: function () { +>produce : () => string +>function () { return "hello"; } : () => string + + return "hello"; +>"hello" : "hello" + + }, + consume: (x) => x.toLowerCase(), +>consume : (x: string) => string +>(x) => x.toLowerCase() : (x: string) => string +>x : string +>x.toLowerCase() : string +>x.toLowerCase : () => string +>x : string +>toLowerCase : () => string + + }, + b: { +>b : { produce: () => { v: string; }; consume: (x: { v: string; }) => string; } +>{ produce: function () { return { v: "hello" }; }, consume: (x) => x.v.toLowerCase(), } : { produce: () => { v: string; }; consume: (x: { v: string; }) => string; } + + produce: function () { +>produce : () => { v: string; } +>function () { return { v: "hello" }; } : () => { v: string; } + + return { v: "hello" }; +>{ v: "hello" } : { v: string; } +>v : string +>"hello" : "hello" + + }, + consume: (x) => x.v.toLowerCase(), +>consume : (x: { v: string; }) => string +>(x) => x.v.toLowerCase() : (x: { v: string; }) => string +>x : { v: string; } +>x.v.toLowerCase() : string +>x.v.toLowerCase : () => string +>x.v : string +>x : { v: string; } +>v : string +>toLowerCase : () => string + + }, +}); + +const res3 = f({ +>res3 : { a: string; b: { v: string; }; } +>f({ a: { produce() { return "hello"; }, consume: (x) => x.toLowerCase(), }, b: { produce() { return { v: "hello" }; }, consume: (x) => x.v.toLowerCase(), },}) : { a: string; b: { v: string; }; } +>f : (arg: { [K in keyof T]: { produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; }) => T +>{ a: { produce() { return "hello"; }, consume: (x) => x.toLowerCase(), }, b: { produce() { return { v: "hello" }; }, consume: (x) => x.v.toLowerCase(), },} : { a: { produce(): string; consume: (x: string) => string; }; b: { produce(): { v: string; }; consume: (x: { v: string; }) => string; }; } + + a: { +>a : { produce(): string; consume: (x: string) => string; } +>{ produce() { return "hello"; }, consume: (x) => x.toLowerCase(), } : { produce(): string; consume: (x: string) => string; } + + produce() { +>produce : () => string + + return "hello"; +>"hello" : "hello" + + }, + consume: (x) => x.toLowerCase(), +>consume : (x: string) => string +>(x) => x.toLowerCase() : (x: string) => string +>x : string +>x.toLowerCase() : string +>x.toLowerCase : () => string +>x : string +>toLowerCase : () => string + + }, + b: { +>b : { produce(): { v: string; }; consume: (x: { v: string; }) => string; } +>{ produce() { return { v: "hello" }; }, consume: (x) => x.v.toLowerCase(), } : { produce(): { v: string; }; consume: (x: { v: string; }) => string; } + + produce() { +>produce : () => { v: string; } + + return { v: "hello" }; +>{ v: "hello" } : { v: string; } +>v : string +>"hello" : "hello" + + }, + consume: (x) => x.v.toLowerCase(), +>consume : (x: { v: string; }) => string +>(x) => x.v.toLowerCase() : (x: { v: string; }) => string +>x : { v: string; } +>x.v.toLowerCase() : string +>x.v.toLowerCase : () => string +>x.v : string +>x : { v: string; } +>v : string +>toLowerCase : () => string + + }, +}); + +declare function f2( +>f2 : (arg: [...{ [K in keyof T]: { produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; }]) => T + + arg: [ +>arg : [...{ [K in keyof T]: { produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; }] + + ...{ + [K in keyof T]: { + produce: (n: string) => T[K]; +>produce : (n: string) => T[K] +>n : string + + consume: (x: T[K]) => void; +>consume : (x: T[K]) => void +>x : T[K] + + }; + } + ] +): T; + +const res4 = f2([ +>res4 : [string, { v: string; }] +>f2([ { produce: (n) => n, consume: (x) => x.toLowerCase(), }, { produce: (n) => ({ v: n }), consume: (x) => x.v.toLowerCase(), },]) : [string, { v: string; }] +>f2 : (arg: [...{ [K in keyof T]: { produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; }]) => T +>[ { produce: (n) => n, consume: (x) => x.toLowerCase(), }, { produce: (n) => ({ v: n }), consume: (x) => x.v.toLowerCase(), },] : [{ produce: (n: string) => string; consume: (x: unknown) => any; }, { produce: (n: string) => { v: string; }; consume: (x: unknown) => any; }] + { +>{ produce: (n) => n, consume: (x) => x.toLowerCase(), } : { produce: (n: string) => string; consume: (x: unknown) => any; } + + produce: (n) => n, +>produce : (n: string) => string +>(n) => n : (n: string) => string +>n : string +>n : string + + consume: (x) => x.toLowerCase(), +>consume : (x: unknown) => any +>(x) => x.toLowerCase() : (x: unknown) => any +>x : unknown +>x.toLowerCase() : any +>x.toLowerCase : any +>x : unknown +>toLowerCase : any + + }, + { +>{ produce: (n) => ({ v: n }), consume: (x) => x.v.toLowerCase(), } : { produce: (n: string) => { v: string; }; consume: (x: unknown) => any; } + + produce: (n) => ({ v: n }), +>produce : (n: string) => { v: string; } +>(n) => ({ v: n }) : (n: string) => { v: string; } +>n : string +>({ v: n }) : { v: string; } +>{ v: n } : { v: string; } +>v : string +>n : string + + consume: (x) => x.v.toLowerCase(), +>consume : (x: unknown) => any +>(x) => x.v.toLowerCase() : (x: unknown) => any +>x : unknown +>x.v.toLowerCase() : any +>x.v.toLowerCase : any +>x.v : any +>x : unknown +>v : any +>toLowerCase : any + + }, +]); + +const res5 = f2([ +>res5 : [string, { v: string; }] +>f2([ { produce: function () { return "hello"; }, consume: (x) => x.toLowerCase(), }, { produce: function () { return { v: "hello" }; }, consume: (x) => x.v.toLowerCase(), },]) : [string, { v: string; }] +>f2 : (arg: [...{ [K in keyof T]: { produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; }]) => T +>[ { produce: function () { return "hello"; }, consume: (x) => x.toLowerCase(), }, { produce: function () { return { v: "hello" }; }, consume: (x) => x.v.toLowerCase(), },] : [{ produce: () => string; consume: (x: unknown) => any; }, { produce: () => { v: string; }; consume: (x: unknown) => any; }] + { +>{ produce: function () { return "hello"; }, consume: (x) => x.toLowerCase(), } : { produce: () => string; consume: (x: unknown) => any; } + + produce: function () { +>produce : () => string +>function () { return "hello"; } : () => string + + return "hello"; +>"hello" : "hello" + + }, + consume: (x) => x.toLowerCase(), +>consume : (x: unknown) => any +>(x) => x.toLowerCase() : (x: unknown) => any +>x : unknown +>x.toLowerCase() : any +>x.toLowerCase : any +>x : unknown +>toLowerCase : any + + }, + { +>{ produce: function () { return { v: "hello" }; }, consume: (x) => x.v.toLowerCase(), } : { produce: () => { v: string; }; consume: (x: unknown) => any; } + + produce: function () { +>produce : () => { v: string; } +>function () { return { v: "hello" }; } : () => { v: string; } + + return { v: "hello" }; +>{ v: "hello" } : { v: string; } +>v : string +>"hello" : "hello" + + }, + consume: (x) => x.v.toLowerCase(), +>consume : (x: unknown) => any +>(x) => x.v.toLowerCase() : (x: unknown) => any +>x : unknown +>x.v.toLowerCase() : any +>x.v.toLowerCase : any +>x.v : any +>x : unknown +>v : any +>toLowerCase : any + + }, +]); + +const res6 = f2([ +>res6 : [string, { v: string; }] +>f2([ { produce() { return "hello"; }, consume: (x) => x.toLowerCase(), }, { produce() { return { v: "hello" }; }, consume: (x) => x.v.toLowerCase(), },]) : [string, { v: string; }] +>f2 : (arg: [...{ [K in keyof T]: { produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; }]) => T +>[ { produce() { return "hello"; }, consume: (x) => x.toLowerCase(), }, { produce() { return { v: "hello" }; }, consume: (x) => x.v.toLowerCase(), },] : [{ produce(): string; consume: (x: unknown) => any; }, { produce(): { v: string; }; consume: (x: unknown) => any; }] + { +>{ produce() { return "hello"; }, consume: (x) => x.toLowerCase(), } : { produce(): string; consume: (x: unknown) => any; } + + produce() { +>produce : () => string + + return "hello"; +>"hello" : "hello" + + }, + consume: (x) => x.toLowerCase(), +>consume : (x: unknown) => any +>(x) => x.toLowerCase() : (x: unknown) => any +>x : unknown +>x.toLowerCase() : any +>x.toLowerCase : any +>x : unknown +>toLowerCase : any + + }, + { +>{ produce() { return { v: "hello" }; }, consume: (x) => x.v.toLowerCase(), } : { produce(): { v: string; }; consume: (x: unknown) => any; } + + produce() { +>produce : () => { v: string; } + + return { v: "hello" }; +>{ v: "hello" } : { v: string; } +>v : string +>"hello" : "hello" + + }, + consume: (x) => x.v.toLowerCase(), +>consume : (x: unknown) => any +>(x) => x.v.toLowerCase() : (x: unknown) => any +>x : unknown +>x.v.toLowerCase() : any +>x.v.toLowerCase : any +>x.v : any +>x : unknown +>v : any +>toLowerCase : any + + }, +]); + +declare function f3( +>f3 : (arg: { [K in keyof T]: { other: number; produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; }) => T + + arg: { +>arg : { [K in keyof T]: { other: number; produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; } + + [K in keyof T]: { + other: number, +>other : number + + produce: (n: string) => T[K]; +>produce : (n: string) => T[K] +>n : string + + consume: (x: T[K]) => void; +>consume : (x: T[K]) => void +>x : T[K] + + }; + } +): T; + +const res7 = f3({ +>res7 : { a: string; b: { v: string; }; } +>f3({ a: { other: 42, produce: (n) => n, consume: (x) => x.toLowerCase(), }, b: { other: 100, produce: (n) => ({ v: n }), consume: (x) => x.v.toLowerCase(), },}) : { a: string; b: { v: string; }; } +>f3 : (arg: { [K in keyof T]: { other: number; produce: (n: string) => T[K]; consume: (x: T[K]) => void; }; }) => T +>{ a: { other: 42, produce: (n) => n, consume: (x) => x.toLowerCase(), }, b: { other: 100, produce: (n) => ({ v: n }), consume: (x) => x.v.toLowerCase(), },} : { a: { other: number; produce: (n: string) => string; consume: (x: string) => string; }; b: { other: number; produce: (n: string) => { v: string; }; consume: (x: { v: string; }) => string; }; } + + a: { +>a : { other: number; produce: (n: string) => string; consume: (x: string) => string; } +>{ other: 42, produce: (n) => n, consume: (x) => x.toLowerCase(), } : { other: number; produce: (n: string) => string; consume: (x: string) => string; } + + other: 42, +>other : number +>42 : 42 + + produce: (n) => n, +>produce : (n: string) => string +>(n) => n : (n: string) => string +>n : string +>n : string + + consume: (x) => x.toLowerCase(), +>consume : (x: string) => string +>(x) => x.toLowerCase() : (x: string) => string +>x : string +>x.toLowerCase() : string +>x.toLowerCase : () => string +>x : string +>toLowerCase : () => string + + }, + b: { +>b : { other: number; produce: (n: string) => { v: string; }; consume: (x: { v: string; }) => string; } +>{ other: 100, produce: (n) => ({ v: n }), consume: (x) => x.v.toLowerCase(), } : { other: number; produce: (n: string) => { v: string; }; consume: (x: { v: string; }) => string; } + + other: 100, +>other : number +>100 : 100 + + produce: (n) => ({ v: n }), +>produce : (n: string) => { v: string; } +>(n) => ({ v: n }) : (n: string) => { v: string; } +>n : string +>({ v: n }) : { v: string; } +>{ v: n } : { v: string; } +>v : string +>n : string + + consume: (x) => x.v.toLowerCase(), +>consume : (x: { v: string; }) => string +>(x) => x.v.toLowerCase() : (x: { v: string; }) => string +>x : { v: string; } +>x.v.toLowerCase() : string +>x.v.toLowerCase : () => string +>x.v : string +>x : { v: string; } +>v : string +>toLowerCase : () => string + + }, +}); + diff --git a/tests/baselines/reference/mappedTypeContextualTypesApplied.errors.txt b/tests/baselines/reference/mappedTypeContextualTypesApplied.errors.txt new file mode 100644 index 0000000000000..6501a987f4589 --- /dev/null +++ b/tests/baselines/reference/mappedTypeContextualTypesApplied.errors.txt @@ -0,0 +1,38 @@ +tests/cases/compiler/mappedTypeContextualTypesApplied.ts(18,10): error TS7023: 'foo' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. +tests/cases/compiler/mappedTypeContextualTypesApplied.ts(18,15): error TS7006: Parameter 's' implicitly has an 'any' type. +tests/cases/compiler/mappedTypeContextualTypesApplied.ts(19,10): error TS7023: 'foo' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. +tests/cases/compiler/mappedTypeContextualTypesApplied.ts(19,15): error TS7006: Parameter 's' implicitly has an 'any' type. + + +==== tests/cases/compiler/mappedTypeContextualTypesApplied.ts (4 errors) ==== + type TakeString = (s: string) => any; + + // Various functions accepting an object whose properties are TakeString functions. + // Note these all use mapped types. + declare function mapped1(obj: T): void; + declare function mapped2(obj: T): void; + declare function mapped3(obj: T): void; + declare function mapped4(obj: T & {[P in keyof T]: TakeString}): void; + declare function mapped5(obj: T & {[P in K]: TakeString}): void; + declare function mapped6(obj: {[P in K]: TakeString}): void; + declare function mapped7(obj: {[P in K]: TakeString}): void; + declare function mapped8(obj: {[P in K]: TakeString}): void; + declare function mapped9(obj: {[P in K]: TakeString}): void; + + mapped1({foo: s => 42}); + mapped2({foo: s => 42}); + mapped3({foo: s => 42}); + mapped4({foo: s => 42}); + ~~~ +!!! error TS7023: 'foo' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. + ~ +!!! error TS7006: Parameter 's' implicitly has an 'any' type. + mapped5({foo: s => 42}); + ~~~ +!!! error TS7023: 'foo' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. + ~ +!!! error TS7006: Parameter 's' implicitly has an 'any' type. + mapped6({foo: s => 42}); + mapped7({foo: s => 42}); + mapped8({foo: s => 42}); + mapped9({foo: s => 42}); \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeContextualTypesApplied.types b/tests/baselines/reference/mappedTypeContextualTypesApplied.types index dde424dab5d7b..4b2da2515234f 100644 --- a/tests/baselines/reference/mappedTypeContextualTypesApplied.types +++ b/tests/baselines/reference/mappedTypeContextualTypesApplied.types @@ -71,19 +71,19 @@ mapped3({foo: s => 42}); mapped4({foo: s => 42}); >mapped4({foo: s => 42}) : void >mapped4 : (obj: T & { [P in keyof T]: TakeString; }) => void ->{foo: s => 42} : { foo: (s: string) => number; } ->foo : (s: string) => number ->s => 42 : (s: string) => number ->s : string +>{foo: s => 42} : { foo: (s: any) => any; } +>foo : (s: any) => any +>s => 42 : (s: any) => any +>s : any >42 : 42 mapped5({foo: s => 42}); >mapped5({foo: s => 42}) : void >mapped5 : (obj: T & { [P in K]: TakeString; }) => void ->{foo: s => 42} : { foo: (s: string) => number; } ->foo : (s: string) => number ->s => 42 : (s: string) => number ->s : string +>{foo: s => 42} : { foo: (s: any) => any; } +>foo : (s: any) => any +>s => 42 : (s: any) => any +>s : any >42 : 42 mapped6({foo: s => 42}); diff --git a/tests/baselines/reference/reverseMappedInferenceWithCircularConstraint.errors.txt b/tests/baselines/reference/reverseMappedInferenceWithCircularConstraint.errors.txt new file mode 100644 index 0000000000000..3d021c1599e86 --- /dev/null +++ b/tests/baselines/reference/reverseMappedInferenceWithCircularConstraint.errors.txt @@ -0,0 +1,65 @@ +tests/cases/compiler/reverseMappedInferenceWithCircularConstraint.ts(47,3): error TS2322: Type '"unknown"' is not assignable to type '"pending"'. + + +==== tests/cases/compiler/reverseMappedInferenceWithCircularConstraint.ts (1 errors) ==== + // repro from https://github.com/microsoft/TypeScript/issues/48798 + + type AnyFunction = (...args: any[]) => any; + + type InferNarrowest = T extends any + ? T extends AnyFunction + ? T + : T extends object + ? InferNarrowestObject + : T + : never; + + type InferNarrowestObject = { + readonly [K in keyof T]: InferNarrowest; + }; + + type Config> = { + states: { + [StateKey in keyof TState]: { + on?: {}; + }; + }; + } & { + initial: keyof TState; + }; + + type Prop = K extends keyof T ? T[K] : never; + + const createMachine = >( + _config: InferNarrowestObject + ): void => {}; + + createMachine({ + initial: "pending", + states: { + pending: { + on: { + done() { + return "noData"; + }, + }, + }, + }, + }); + + createMachine({ + initial: "unknown", // error + ~~~~~~~ +!!! error TS2322: Type '"unknown"' is not assignable to type '"pending"'. +!!! related TS6500 tests/cases/compiler/reverseMappedInferenceWithCircularConstraint.ts:24:3: The expected type comes from property 'initial' which is declared here on type 'InferNarrowestObject>' + states: { + pending: { + on: { + done() { + return "noData"; + }, + }, + }, + }, + }); + \ No newline at end of file diff --git a/tests/baselines/reference/reverseMappedInferenceWithCircularConstraint.symbols b/tests/baselines/reference/reverseMappedInferenceWithCircularConstraint.symbols new file mode 100644 index 0000000000000..4174aa46c2ddf --- /dev/null +++ b/tests/baselines/reference/reverseMappedInferenceWithCircularConstraint.symbols @@ -0,0 +1,142 @@ +=== tests/cases/compiler/reverseMappedInferenceWithCircularConstraint.ts === +// repro from https://github.com/microsoft/TypeScript/issues/48798 + +type AnyFunction = (...args: any[]) => any; +>AnyFunction : Symbol(AnyFunction, Decl(reverseMappedInferenceWithCircularConstraint.ts, 0, 0)) +>args : Symbol(args, Decl(reverseMappedInferenceWithCircularConstraint.ts, 2, 20)) + +type InferNarrowest = T extends any +>InferNarrowest : Symbol(InferNarrowest, Decl(reverseMappedInferenceWithCircularConstraint.ts, 2, 43)) +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 4, 20)) +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 4, 20)) + + ? T extends AnyFunction +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 4, 20)) +>AnyFunction : Symbol(AnyFunction, Decl(reverseMappedInferenceWithCircularConstraint.ts, 0, 0)) + + ? T +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 4, 20)) + + : T extends object +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 4, 20)) + + ? InferNarrowestObject +>InferNarrowestObject : Symbol(InferNarrowestObject, Decl(reverseMappedInferenceWithCircularConstraint.ts, 10, 10)) +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 4, 20)) + + : T +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 4, 20)) + + : never; + +type InferNarrowestObject = { +>InferNarrowestObject : Symbol(InferNarrowestObject, Decl(reverseMappedInferenceWithCircularConstraint.ts, 10, 10)) +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 12, 26)) + + readonly [K in keyof T]: InferNarrowest; +>K : Symbol(K, Decl(reverseMappedInferenceWithCircularConstraint.ts, 13, 12)) +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 12, 26)) +>InferNarrowest : Symbol(InferNarrowest, Decl(reverseMappedInferenceWithCircularConstraint.ts, 2, 43)) +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 12, 26)) +>K : Symbol(K, Decl(reverseMappedInferenceWithCircularConstraint.ts, 13, 12)) + +}; + +type Config> = { +>Config : Symbol(Config, Decl(reverseMappedInferenceWithCircularConstraint.ts, 14, 2)) +>TGlobal : Symbol(TGlobal, Decl(reverseMappedInferenceWithCircularConstraint.ts, 16, 12)) +>TState : Symbol(TState, Decl(reverseMappedInferenceWithCircularConstraint.ts, 16, 20)) +>Prop : Symbol(Prop, Decl(reverseMappedInferenceWithCircularConstraint.ts, 24, 2)) +>TGlobal : Symbol(TGlobal, Decl(reverseMappedInferenceWithCircularConstraint.ts, 16, 12)) + + states: { +>states : Symbol(states, Decl(reverseMappedInferenceWithCircularConstraint.ts, 16, 58)) + + [StateKey in keyof TState]: { +>StateKey : Symbol(StateKey, Decl(reverseMappedInferenceWithCircularConstraint.ts, 18, 5)) +>TState : Symbol(TState, Decl(reverseMappedInferenceWithCircularConstraint.ts, 16, 20)) + + on?: {}; +>on : Symbol(on, Decl(reverseMappedInferenceWithCircularConstraint.ts, 18, 33)) + + }; + }; +} & { + initial: keyof TState; +>initial : Symbol(initial, Decl(reverseMappedInferenceWithCircularConstraint.ts, 22, 5)) +>TState : Symbol(TState, Decl(reverseMappedInferenceWithCircularConstraint.ts, 16, 20)) + +}; + +type Prop = K extends keyof T ? T[K] : never; +>Prop : Symbol(Prop, Decl(reverseMappedInferenceWithCircularConstraint.ts, 24, 2)) +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 26, 10)) +>K : Symbol(K, Decl(reverseMappedInferenceWithCircularConstraint.ts, 26, 12)) +>K : Symbol(K, Decl(reverseMappedInferenceWithCircularConstraint.ts, 26, 12)) +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 26, 10)) +>T : Symbol(T, Decl(reverseMappedInferenceWithCircularConstraint.ts, 26, 10)) +>K : Symbol(K, Decl(reverseMappedInferenceWithCircularConstraint.ts, 26, 12)) + +const createMachine = >( +>createMachine : Symbol(createMachine, Decl(reverseMappedInferenceWithCircularConstraint.ts, 28, 5)) +>TConfig : Symbol(TConfig, Decl(reverseMappedInferenceWithCircularConstraint.ts, 28, 23)) +>Config : Symbol(Config, Decl(reverseMappedInferenceWithCircularConstraint.ts, 14, 2)) +>TConfig : Symbol(TConfig, Decl(reverseMappedInferenceWithCircularConstraint.ts, 28, 23)) + + _config: InferNarrowestObject +>_config : Symbol(_config, Decl(reverseMappedInferenceWithCircularConstraint.ts, 28, 56)) +>InferNarrowestObject : Symbol(InferNarrowestObject, Decl(reverseMappedInferenceWithCircularConstraint.ts, 10, 10)) +>TConfig : Symbol(TConfig, Decl(reverseMappedInferenceWithCircularConstraint.ts, 28, 23)) + +): void => {}; + +createMachine({ +>createMachine : Symbol(createMachine, Decl(reverseMappedInferenceWithCircularConstraint.ts, 28, 5)) + + initial: "pending", +>initial : Symbol(initial, Decl(reverseMappedInferenceWithCircularConstraint.ts, 32, 15)) + + states: { +>states : Symbol(states, Decl(reverseMappedInferenceWithCircularConstraint.ts, 33, 21)) + + pending: { +>pending : Symbol(pending, Decl(reverseMappedInferenceWithCircularConstraint.ts, 34, 11)) + + on: { +>on : Symbol(on, Decl(reverseMappedInferenceWithCircularConstraint.ts, 35, 14)) + + done() { +>done : Symbol(done, Decl(reverseMappedInferenceWithCircularConstraint.ts, 36, 11)) + + return "noData"; + }, + }, + }, + }, +}); + +createMachine({ +>createMachine : Symbol(createMachine, Decl(reverseMappedInferenceWithCircularConstraint.ts, 28, 5)) + + initial: "unknown", // error +>initial : Symbol(initial, Decl(reverseMappedInferenceWithCircularConstraint.ts, 45, 15)) + + states: { +>states : Symbol(states, Decl(reverseMappedInferenceWithCircularConstraint.ts, 46, 21)) + + pending: { +>pending : Symbol(pending, Decl(reverseMappedInferenceWithCircularConstraint.ts, 47, 11)) + + on: { +>on : Symbol(on, Decl(reverseMappedInferenceWithCircularConstraint.ts, 48, 14)) + + done() { +>done : Symbol(done, Decl(reverseMappedInferenceWithCircularConstraint.ts, 49, 11)) + + return "noData"; + }, + }, + }, + }, +}); + diff --git a/tests/baselines/reference/reverseMappedInferenceWithCircularConstraint.types b/tests/baselines/reference/reverseMappedInferenceWithCircularConstraint.types new file mode 100644 index 0000000000000..235cabe502adb --- /dev/null +++ b/tests/baselines/reference/reverseMappedInferenceWithCircularConstraint.types @@ -0,0 +1,119 @@ +=== tests/cases/compiler/reverseMappedInferenceWithCircularConstraint.ts === +// repro from https://github.com/microsoft/TypeScript/issues/48798 + +type AnyFunction = (...args: any[]) => any; +>AnyFunction : (...args: any[]) => any +>args : any[] + +type InferNarrowest = T extends any +>InferNarrowest : InferNarrowest + + ? T extends AnyFunction + ? T + : T extends object + ? InferNarrowestObject + : T + : never; + +type InferNarrowestObject = { +>InferNarrowestObject : InferNarrowestObject + + readonly [K in keyof T]: InferNarrowest; +}; + +type Config> = { +>Config : Config + + states: { +>states : { [StateKey in keyof TState]: { on?: {} | undefined; }; } + + [StateKey in keyof TState]: { + on?: {}; +>on : {} | undefined + + }; + }; +} & { + initial: keyof TState; +>initial : keyof TState + +}; + +type Prop = K extends keyof T ? T[K] : never; +>Prop : Prop + +const createMachine = >( +>createMachine : >>(_config: InferNarrowestObject) => void +>>( _config: InferNarrowestObject): void => {} : >>(_config: InferNarrowestObject) => void + + _config: InferNarrowestObject +>_config : InferNarrowestObject + +): void => {}; + +createMachine({ +>createMachine({ initial: "pending", states: { pending: { on: { done() { return "noData"; }, }, }, },}) : void +>createMachine : >>(_config: InferNarrowestObject) => void +>{ initial: "pending", states: { pending: { on: { done() { return "noData"; }, }, }, },} : { initial: "pending"; states: { pending: { on: { done(): "noData"; }; }; }; } + + initial: "pending", +>initial : "pending" +>"pending" : "pending" + + states: { +>states : { pending: { on: { done(): "noData"; }; }; } +>{ pending: { on: { done() { return "noData"; }, }, }, } : { pending: { on: { done(): "noData"; }; }; } + + pending: { +>pending : { on: { done(): "noData"; }; } +>{ on: { done() { return "noData"; }, }, } : { on: { done(): "noData"; }; } + + on: { +>on : { done(): "noData"; } +>{ done() { return "noData"; }, } : { done(): "noData"; } + + done() { +>done : () => "noData" + + return "noData"; +>"noData" : "noData" + + }, + }, + }, + }, +}); + +createMachine({ +>createMachine({ initial: "unknown", // error states: { pending: { on: { done() { return "noData"; }, }, }, },}) : void +>createMachine : >>(_config: InferNarrowestObject) => void +>{ initial: "unknown", // error states: { pending: { on: { done() { return "noData"; }, }, }, },} : { initial: "unknown"; states: { pending: { on: { done(): string; }; }; }; } + + initial: "unknown", // error +>initial : "unknown" +>"unknown" : "unknown" + + states: { +>states : { pending: { on: { done(): string; }; }; } +>{ pending: { on: { done() { return "noData"; }, }, }, } : { pending: { on: { done(): string; }; }; } + + pending: { +>pending : { on: { done(): string; }; } +>{ on: { done() { return "noData"; }, }, } : { on: { done(): string; }; } + + on: { +>on : { done(): string; } +>{ done() { return "noData"; }, } : { done(): string; } + + done() { +>done : () => string + + return "noData"; +>"noData" : "noData" + + }, + }, + }, + }, +}); + diff --git a/tests/baselines/reference/reverseMappedPartiallyInferableTypes.types b/tests/baselines/reference/reverseMappedPartiallyInferableTypes.types index 2bb2353cb79f9..093f805671520 100644 --- a/tests/baselines/reference/reverseMappedPartiallyInferableTypes.types +++ b/tests/baselines/reference/reverseMappedPartiallyInferableTypes.types @@ -206,8 +206,8 @@ const obj2 = id({ // No properties have inferable types const obj3 = id({ ->obj3 : Mapped ->id({ foo: { contains(k) { return k.length > 0; } }}) : Mapped +>obj3 : Mapped<{ foo: unknown; }> +>id({ foo: { contains(k) { return k.length > 0; } }}) : Mapped<{ foo: unknown; }> >id : (arg: Mapped) => Mapped >{ foo: { contains(k) { return k.length > 0; } }} : { foo: { contains(k: unknown): boolean; }; } diff --git a/tests/cases/compiler/reverseMappedInferenceWithCircularConstraint.ts b/tests/cases/compiler/reverseMappedInferenceWithCircularConstraint.ts new file mode 100644 index 0000000000000..dae226e89632e --- /dev/null +++ b/tests/cases/compiler/reverseMappedInferenceWithCircularConstraint.ts @@ -0,0 +1,60 @@ +// @strict: true +// @noEmit: true + +// repro from https://github.com/microsoft/TypeScript/issues/48798 + +type AnyFunction = (...args: any[]) => any; + +type InferNarrowest = T extends any + ? T extends AnyFunction + ? T + : T extends object + ? InferNarrowestObject + : T + : never; + +type InferNarrowestObject = { + readonly [K in keyof T]: InferNarrowest; +}; + +type Config> = { + states: { + [StateKey in keyof TState]: { + on?: {}; + }; + }; +} & { + initial: keyof TState; +}; + +type Prop = K extends keyof T ? T[K] : never; + +const createMachine = >( + _config: InferNarrowestObject +): void => {}; + +createMachine({ + initial: "pending", + states: { + pending: { + on: { + done() { + return "noData"; + }, + }, + }, + }, +}); + +createMachine({ + initial: "unknown", // error + states: { + pending: { + on: { + done() { + return "noData"; + }, + }, + }, + }, +}); diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts b/tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts new file mode 100644 index 0000000000000..ae6c64b5cc94e --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts @@ -0,0 +1,129 @@ +// @strict: true +// @declaration: true + +// repro cases based on https://github.com/microsoft/TypeScript/issues/53018 + +declare function f( + arg: { + [K in keyof T]: { + produce: (n: string) => T[K]; + consume: (x: T[K]) => void; + }; + } +): T; + +const res1 = f({ + a: { + produce: (n) => n, + consume: (x) => x.toLowerCase(), + }, + b: { + produce: (n) => ({ v: n }), + consume: (x) => x.v.toLowerCase(), + }, +}); + +const res2 = f({ + a: { + produce: function () { + return "hello"; + }, + consume: (x) => x.toLowerCase(), + }, + b: { + produce: function () { + return { v: "hello" }; + }, + consume: (x) => x.v.toLowerCase(), + }, +}); + +const res3 = f({ + a: { + produce() { + return "hello"; + }, + consume: (x) => x.toLowerCase(), + }, + b: { + produce() { + return { v: "hello" }; + }, + consume: (x) => x.v.toLowerCase(), + }, +}); + +declare function f2( + arg: [ + ...{ + [K in keyof T]: { + produce: (n: string) => T[K]; + consume: (x: T[K]) => void; + }; + } + ] +): T; + +const res4 = f2([ + { + produce: (n) => n, + consume: (x) => x.toLowerCase(), + }, + { + produce: (n) => ({ v: n }), + consume: (x) => x.v.toLowerCase(), + }, +]); + +const res5 = f2([ + { + produce: function () { + return "hello"; + }, + consume: (x) => x.toLowerCase(), + }, + { + produce: function () { + return { v: "hello" }; + }, + consume: (x) => x.v.toLowerCase(), + }, +]); + +const res6 = f2([ + { + produce() { + return "hello"; + }, + consume: (x) => x.toLowerCase(), + }, + { + produce() { + return { v: "hello" }; + }, + consume: (x) => x.v.toLowerCase(), + }, +]); + +declare function f3( + arg: { + [K in keyof T]: { + other: number, + produce: (n: string) => T[K]; + consume: (x: T[K]) => void; + }; + } +): T; + +const res7 = f3({ + a: { + other: 42, + produce: (n) => n, + consume: (x) => x.toLowerCase(), + }, + b: { + other: 100, + produce: (n) => ({ v: n }), + consume: (x) => x.v.toLowerCase(), + }, +}); From 557cd9908992f3cbb803c2ab05c5609516824396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 26 Apr 2023 15:38:34 +0200 Subject: [PATCH 2/6] use more specialized `Debug.assertNode` --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 71f28a8d63e0d..ad15fabf97dd0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24117,7 +24117,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (intraExpressionInferenceSites?.length) { const templateType = (getApparentTypeOfContextualType(sourceValueDeclaration.parent.parent as Expression, ContextFlags.NoConstraints) as MappedType).templateType; if (templateType) { - Debug.assert(isExpressionNode(sourceValueDeclaration)); + Debug.assertNode(sourceValueDeclaration, isExpressionNode); pushContextualType(sourceValueDeclaration as any as Expression, templateType, /*isCache*/ false); inferFromIntraExpressionSites([inference], intraExpressionInferenceSites); popContextualType(); From b7eae05a77cebc8fb9b6f965d836ebb0db341c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 24 May 2023 23:30:06 +0200 Subject: [PATCH 3/6] check freshness of the source type before attempting to infer from intra expressions --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 50b63a8cbba2f..5afb41e104fba 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24129,8 +24129,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const templateType = getTemplateTypeFromMappedType(target); const inference = createInferenceInfo(typeParameter); inferTypes([inference], sourceType, templateType); - const sourceValueDeclaration = sourceType.symbol?.valueDeclaration; - if (sourceValueDeclaration) { + if (getObjectFlags(sourceType) & (ObjectFlags.FreshLiteral | ObjectFlags.ArrayLiteral)) { + const sourceValueDeclaration = sourceType.symbol.valueDeclaration!; const intraExpressionInferenceSites = getInferenceContext(sourceValueDeclaration)?.intraExpressionInferenceSites?.filter(site => isNodeDescendantOf(site.node, sourceValueDeclaration)); if (intraExpressionInferenceSites?.length) { const templateType = (getApparentTypeOfContextualType(sourceValueDeclaration.parent.parent as Expression, ContextFlags.NoConstraints) as MappedType).templateType; From a0804b5d0108e7532cc2b66714fd70998abcd56e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 25 May 2023 09:14:37 +0200 Subject: [PATCH 4/6] get value declaration from the passed in argument --- src/compiler/checker.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5afb41e104fba..ec8afde0f0fff 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13122,12 +13122,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const modifiers = getMappedTypeModifiers(type.mappedType); const readonlyMask = modifiers & MappedTypeModifiers.IncludeReadonly ? false : true; const optionalMask = modifiers & MappedTypeModifiers.IncludeOptional ? 0 : SymbolFlags.Optional; - const indexInfos = indexInfo ? [createIndexInfo(stringType, inferReverseMappedType(indexInfo.type, type.mappedType, type.constraintType), readonlyMask && indexInfo.isReadonly)] : emptyArray; + const indexInfos = indexInfo ? [createIndexInfo(stringType, inferReverseMappedType(indexInfo.type, type.mappedType, type.constraintType, /*sourceValueDeclaration*/ undefined), readonlyMask && indexInfo.isReadonly)] : emptyArray; const members = createSymbolTable(); for (const prop of getPropertiesOfType(type.source)) { const checkFlags = CheckFlags.ReverseMapped | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0); const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as ReverseMappedSymbol; inferredProp.declarations = prop.declarations; + inferredProp.valueDeclaration = prop.valueDeclaration; inferredProp.links.nameType = getSymbolLinks(prop).nameType; inferredProp.links.propertyType = getTypeOfSymbol(prop); if (type.constraintType.type.flags & TypeFlags.IndexedAccess @@ -24098,10 +24099,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // For arrays and tuples we infer new arrays and tuples where the reverse mapping has been // applied to the element type(s). if (isArrayType(source)) { - return createArrayType(inferReverseMappedType(getTypeArguments(source)[0], target, constraint), isReadonlyArrayType(source)); + return createArrayType(inferReverseMappedType(getTypeArguments(source)[0], target, constraint, /*sourceValueDeclaration*/ undefined), isReadonlyArrayType(source)); } if (isTupleType(source)) { - const elementTypes = map(getElementTypes(source), t => inferReverseMappedType(t, target, constraint)); + const elementTypes = map(getElementTypes(source), t => inferReverseMappedType(t, target, constraint, /*sourceValueDeclaration*/ undefined)); const elementFlags = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ? sameMap(source.target.elementFlags, f => f & ElementFlags.Optional ? ElementFlags.Required : f) : source.target.elementFlags; @@ -24119,24 +24120,26 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol) { const links = getSymbolLinks(symbol); if (!links.type) { - links.type = inferReverseMappedType(symbol.links.propertyType, symbol.links.mappedType, symbol.links.constraintType); + links.type = inferReverseMappedType(symbol.links.propertyType, symbol.links.mappedType, symbol.links.constraintType, symbol.valueDeclaration); } return links.type; } - function inferReverseMappedType(sourceType: Type, target: MappedType, constraint: IndexType): Type { + function inferReverseMappedType(sourceType: Type, target: MappedType, constraint: IndexType, sourceValueDeclaration: Declaration | undefined): Type { const typeParameter = getIndexedAccessType(constraint.type, getTypeParameterFromMappedType(target)) as TypeParameter; const templateType = getTemplateTypeFromMappedType(target); const inference = createInferenceInfo(typeParameter); inferTypes([inference], sourceType, templateType); - if (getObjectFlags(sourceType) & (ObjectFlags.FreshLiteral | ObjectFlags.ArrayLiteral)) { - const sourceValueDeclaration = sourceType.symbol.valueDeclaration!; - const intraExpressionInferenceSites = getInferenceContext(sourceValueDeclaration)?.intraExpressionInferenceSites?.filter(site => isNodeDescendantOf(site.node, sourceValueDeclaration)); + if (sourceValueDeclaration && getObjectFlags(sourceType) & (ObjectFlags.FreshLiteral | ObjectFlags.ArrayLiteral)) { + const initializerDeclaration = sourceValueDeclaration.kind === SyntaxKind.PropertyAssignment ? + (sourceValueDeclaration as PropertyAssignment).initializer : + sourceValueDeclaration; + const intraExpressionInferenceSites = getInferenceContext(initializerDeclaration)?.intraExpressionInferenceSites?.filter(site => isNodeDescendantOf(site.node, initializerDeclaration)); if (intraExpressionInferenceSites?.length) { - const templateType = (getApparentTypeOfContextualType(sourceValueDeclaration.parent.parent as Expression, ContextFlags.NoConstraints) as MappedType).templateType; + const templateType = (getApparentTypeOfContextualType(initializerDeclaration.parent.parent as Expression, ContextFlags.NoConstraints) as MappedType).templateType; if (templateType) { - Debug.assertNode(sourceValueDeclaration, isExpressionNode); - pushContextualType(sourceValueDeclaration as any as Expression, templateType, /*isCache*/ false); + Debug.assertNode(initializerDeclaration, isExpressionNode); + pushContextualType(initializerDeclaration as any as Expression, templateType, /*isCache*/ false); inferFromIntraExpressionSites([inference], intraExpressionInferenceSites); popContextualType(); } From 57b54c9ed5a24772d010afb826240144636c74af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 25 May 2023 10:38:41 +0200 Subject: [PATCH 5/6] Ignore `valueDeclaration` in `getDeclarationModifierFlagsFromSymbol` for `ReverseMapped` types --- src/compiler/utilities.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 54b8f92786698..9c3b3849cd495 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -7539,13 +7539,14 @@ export function getCheckFlags(symbol: Symbol): CheckFlags { /** @internal */ export function getDeclarationModifierFlagsFromSymbol(s: Symbol, isWrite = false): ModifierFlags { - if (s.valueDeclaration) { + const checkFlags = getCheckFlags(s); + if (!(checkFlags & CheckFlags.ReverseMapped) && s.valueDeclaration) { const declaration = (isWrite && s.declarations && find(s.declarations, isSetAccessorDeclaration)) || (s.flags & SymbolFlags.GetAccessor && find(s.declarations, isGetAccessorDeclaration)) || s.valueDeclaration; const flags = getCombinedModifierFlags(declaration); return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier; } - if (getCheckFlags(s) & CheckFlags.Synthetic) { + if (checkFlags & CheckFlags.Synthetic) { // NOTE: potentially unchecked cast to TransientSymbol const checkFlags = (s as TransientSymbol).links.checkFlags; const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private : From fae5ae6e1b65c7c00be0424fba7a961ea8247f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 25 May 2023 10:44:28 +0200 Subject: [PATCH 6/6] Add an extra test case for array literals used as values of object reverse mapped types --- ...ionInferencesReverseMappedTypes.errors.txt | 20 ++++++ ...aExpressionInferencesReverseMappedTypes.js | 42 ++++++++++++ ...essionInferencesReverseMappedTypes.symbols | 63 ++++++++++++++++++ ...pressionInferencesReverseMappedTypes.types | 66 +++++++++++++++++++ ...aExpressionInferencesReverseMappedTypes.ts | 20 ++++++ 5 files changed, 211 insertions(+) diff --git a/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.errors.txt b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.errors.txt index 6c7ac51da1b59..559df482aa8c2 100644 --- a/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.errors.txt +++ b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.errors.txt @@ -145,4 +145,24 @@ tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInf consume: (x) => x.v.toLowerCase(), }, }); + + declare function f4( + arg: { + [K in keyof T]: [ + (n: string) => T[K], + (x: T[K]) => void + ]; + } + ): T; + + const res8 = f4({ + a: [ + (n) => n, + (x) => x.toLowerCase(), + ], + b: [ + (n) => ({ v: n }), + (x) => x.v.toLowerCase(), + ], + }); \ No newline at end of file diff --git a/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.js b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.js index 706f7d9b32b04..e15db5b7f718b 100644 --- a/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.js +++ b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.js @@ -125,6 +125,26 @@ const res7 = f3({ consume: (x) => x.v.toLowerCase(), }, }); + +declare function f4( + arg: { + [K in keyof T]: [ + (n: string) => T[K], + (x: T[K]) => void + ]; + } +): T; + +const res8 = f4({ + a: [ + (n) => n, + (x) => x.toLowerCase(), + ], + b: [ + (n) => ({ v: n }), + (x) => x.v.toLowerCase(), + ], +}); //// [intraExpressionInferencesReverseMappedTypes.js] @@ -218,6 +238,16 @@ var res7 = f3({ consume: function (x) { return x.v.toLowerCase(); }, }, }); +var res8 = f4({ + a: [ + function (n) { return n; }, + function (x) { return x.toLowerCase(); }, + ], + b: [ + function (n) { return ({ v: n }); }, + function (x) { return x.v.toLowerCase(); }, + ], +}); //// [intraExpressionInferencesReverseMappedTypes.d.ts] @@ -275,3 +305,15 @@ declare const res7: { v: string; }; }; +declare function f4(arg: { + [K in keyof T]: [ + (n: string) => T[K], + (x: T[K]) => void + ]; +}): T; +declare const res8: { + a: string; + b: { + v: string; + }; +}; diff --git a/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.symbols b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.symbols index 2ad8292ae4c6d..93b98d5818717 100644 --- a/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.symbols +++ b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.symbols @@ -354,3 +354,66 @@ const res7 = f3({ }, }); +declare function f4( +>f4 : Symbol(f4, Decl(intraExpressionInferencesReverseMappedTypes.ts, 125, 3)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 127, 20)) + + arg: { +>arg : Symbol(arg, Decl(intraExpressionInferencesReverseMappedTypes.ts, 127, 23)) + + [K in keyof T]: [ +>K : Symbol(K, Decl(intraExpressionInferencesReverseMappedTypes.ts, 129, 5)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 127, 20)) + + (n: string) => T[K], +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 130, 7)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 127, 20)) +>K : Symbol(K, Decl(intraExpressionInferencesReverseMappedTypes.ts, 129, 5)) + + (x: T[K]) => void +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 131, 7)) +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 127, 20)) +>K : Symbol(K, Decl(intraExpressionInferencesReverseMappedTypes.ts, 129, 5)) + + ]; + } +): T; +>T : Symbol(T, Decl(intraExpressionInferencesReverseMappedTypes.ts, 127, 20)) + +const res8 = f4({ +>res8 : Symbol(res8, Decl(intraExpressionInferencesReverseMappedTypes.ts, 136, 5)) +>f4 : Symbol(f4, Decl(intraExpressionInferencesReverseMappedTypes.ts, 125, 3)) + + a: [ +>a : Symbol(a, Decl(intraExpressionInferencesReverseMappedTypes.ts, 136, 17)) + + (n) => n, +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 138, 5)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 138, 5)) + + (x) => x.toLowerCase(), +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 139, 5)) +>x.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 139, 5)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) + + ], + b: [ +>b : Symbol(b, Decl(intraExpressionInferencesReverseMappedTypes.ts, 140, 4)) + + (n) => ({ v: n }), +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 142, 5)) +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 142, 13)) +>n : Symbol(n, Decl(intraExpressionInferencesReverseMappedTypes.ts, 142, 5)) + + (x) => x.v.toLowerCase(), +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 143, 5)) +>x.v.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>x.v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 142, 13)) +>x : Symbol(x, Decl(intraExpressionInferencesReverseMappedTypes.ts, 143, 5)) +>v : Symbol(v, Decl(intraExpressionInferencesReverseMappedTypes.ts, 142, 13)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) + + ], +}); + diff --git a/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.types b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.types index 868796fd81925..6c40a3084f5ec 100644 --- a/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.types +++ b/tests/baselines/reference/intraExpressionInferencesReverseMappedTypes.types @@ -441,3 +441,69 @@ const res7 = f3({ }, }); +declare function f4( +>f4 : (arg: { [K in keyof T]: [(n: string) => T[K], (x: T[K]) => void]; }) => T + + arg: { +>arg : { [K in keyof T]: [(n: string) => T[K], (x: T[K]) => void]; } + + [K in keyof T]: [ + (n: string) => T[K], +>n : string + + (x: T[K]) => void +>x : T[K] + + ]; + } +): T; + +const res8 = f4({ +>res8 : { a: string; b: { v: string; }; } +>f4({ a: [ (n) => n, (x) => x.toLowerCase(), ], b: [ (n) => ({ v: n }), (x) => x.v.toLowerCase(), ],}) : { a: string; b: { v: string; }; } +>f4 : (arg: { [K in keyof T]: [(n: string) => T[K], (x: T[K]) => void]; }) => T +>{ a: [ (n) => n, (x) => x.toLowerCase(), ], b: [ (n) => ({ v: n }), (x) => x.v.toLowerCase(), ],} : { a: [(n: string) => string, (x: string) => string]; b: [(n: string) => { v: string; }, (x: { v: string; }) => string]; } + + a: [ +>a : [(n: string) => string, (x: string) => string] +>[ (n) => n, (x) => x.toLowerCase(), ] : [(n: string) => string, (x: string) => string] + + (n) => n, +>(n) => n : (n: string) => string +>n : string +>n : string + + (x) => x.toLowerCase(), +>(x) => x.toLowerCase() : (x: string) => string +>x : string +>x.toLowerCase() : string +>x.toLowerCase : () => string +>x : string +>toLowerCase : () => string + + ], + b: [ +>b : [(n: string) => { v: string; }, (x: { v: string; }) => string] +>[ (n) => ({ v: n }), (x) => x.v.toLowerCase(), ] : [(n: string) => { v: string; }, (x: { v: string; }) => string] + + (n) => ({ v: n }), +>(n) => ({ v: n }) : (n: string) => { v: string; } +>n : string +>({ v: n }) : { v: string; } +>{ v: n } : { v: string; } +>v : string +>n : string + + (x) => x.v.toLowerCase(), +>(x) => x.v.toLowerCase() : (x: { v: string; }) => string +>x : { v: string; } +>x.v.toLowerCase() : string +>x.v.toLowerCase : () => string +>x.v : string +>x : { v: string; } +>v : string +>toLowerCase : () => string + + ], +}); + diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts b/tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts index ae6c64b5cc94e..d93dec63b7427 100644 --- a/tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts +++ b/tests/cases/conformance/types/typeRelationships/typeInference/intraExpressionInferencesReverseMappedTypes.ts @@ -127,3 +127,23 @@ const res7 = f3({ consume: (x) => x.v.toLowerCase(), }, }); + +declare function f4( + arg: { + [K in keyof T]: [ + (n: string) => T[K], + (x: T[K]) => void + ]; + } +): T; + +const res8 = f4({ + a: [ + (n) => n, + (x) => x.toLowerCase(), + ], + b: [ + (n) => ({ v: n }), + (x) => x.v.toLowerCase(), + ], +});