diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dfd479127202c..9badb08718b4c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14647,7 +14647,7 @@ 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) || unknownType, readonlyMask && indexInfo.isReadonly)] : emptyArray; + const indexInfos = indexInfo ? [createIndexInfo(stringType, inferReverseMappedType(indexInfo.type, type.mappedType, type.constraintType, type.inferenceConstraintType && getIndexedAccessTypeOrUndefined(type.inferenceConstraintType, indexInfo.keyType)) ?? unknownType, readonlyMask && indexInfo.isReadonly)] : emptyArray; const members = createSymbolTable(); const limitedConstraint = getLimitedConstraint(type); for (const prop of getPropertiesOfType(type.source)) { @@ -14682,6 +14682,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { inferredProp.links.mappedType = type.mappedType; inferredProp.links.constraintType = type.constraintType; } + inferredProp.links.inferenceConstraintType = type.inferenceConstraintType; members.set(prop.escapedName, inferredProp); } setStructuredTypeMembers(type, members, emptyArray, emptyArray, indexInfos); @@ -14961,13 +14962,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { else if ((type as ObjectType).objectFlags & ObjectFlags.ClassOrInterface) { resolveClassOrInterfaceMembers(type as InterfaceType); } - else if ((type as ReverseMappedType).objectFlags & ObjectFlags.ReverseMapped) { + else if ((type as ObjectType).objectFlags & ObjectFlags.ReverseMapped) { resolveReverseMappedTypeMembers(type as ReverseMappedType); } else if ((type as ObjectType).objectFlags & ObjectFlags.Anonymous) { resolveAnonymousTypeMembers(type as AnonymousType); } - else if ((type as MappedType).objectFlags & ObjectFlags.Mapped) { + else if ((type as ObjectType).objectFlags & ObjectFlags.Mapped) { resolveMappedTypeMembers(type as MappedType); } else { @@ -26428,21 +26429,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { - links.type = inferReverseMappedType(symbol.links.propertyType, symbol.links.mappedType, symbol.links.constraintType) || unknownType; + const reverseConstraint = symbol.links.inferenceConstraintType && getIndexedAccessTypeOrUndefined(symbol.links.inferenceConstraintType, getStringLiteralType(unescapeLeadingUnderscores(symbol.escapedName))); + links.type = inferReverseMappedType(symbol.links.propertyType, symbol.links.mappedType, symbol.links.constraintType, reverseConstraint) ?? unknownType; } return links.type; } - function inferReverseMappedTypeWorker(sourceType: Type, target: MappedType, constraint: IndexType): Type { + function inferReverseMappedTypeWorker(sourceType: Type, target: MappedType, constraint: IndexType, reverseMappedConstraint: Type | undefined): Type { const typeParameter = getIndexedAccessType(constraint.type, getTypeParameterFromMappedType(target)) as TypeParameter; const templateType = getTemplateTypeFromMappedType(target); const inference = createInferenceInfo(typeParameter); inferTypes([inference], sourceType, templateType); - return getTypeFromInference(inference) || unknownType; + return getTypeFromInference(inference) ?? reverseMappedConstraint ?? unknownType; } - function inferReverseMappedType(source: Type, target: MappedType, constraint: IndexType): Type | undefined { - const cacheKey = source.id + "," + target.id + "," + constraint.id; + function inferReverseMappedType(source: Type, target: MappedType, constraint: IndexType, reverseMappedConstraint?: Type): Type | undefined { + const cacheKey = source.id + "," + target.id + "," + constraint.id + "," + (reverseMappedConstraint?.id ?? "0"); if (reverseMappedCache.has(cacheKey)) { return reverseMappedCache.get(cacheKey) || unknownType; } @@ -26453,7 +26455,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isDeeplyNestedType(target, reverseMappedTargetStack, reverseMappedTargetStack.length, 2)) reverseExpandingFlags |= ExpandingFlags.Target; let type; if (reverseExpandingFlags !== ExpandingFlags.Both) { - type = inferReverseMappedTypeWorker(source, target, constraint); + type = inferReverseMappedTypeWorker(source, target, constraint, reverseMappedConstraint); } reverseMappedSourceStack.pop(); reverseMappedTargetStack.pop(); @@ -27579,6 +27581,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (constraint) { const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper); if (inferredType) { + inferredType = cloneWithInferenceConstraintIfNeeded(inferredType, instantiatedConstraint); const constraintWithThis = getTypeWithThisArgument(instantiatedConstraint, inferredType); if (!context.compareTypes(inferredType, constraintWithThis)) { // If we have a pure return type inference, we may succeed by removing constituents of the inferred type @@ -27597,6 +27600,27 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return inference.inferredType; + + function cloneWithInferenceConstraintIfNeeded(type: Type, inferenceConstraintType: Type) { + if (!(type.flags & TypeFlags.Object) || !((type as ObjectType).objectFlags & ObjectFlags.ReverseMapped)) { + return type; + } + const reversed = type as ReverseMappedType; + const cacheKey = reversed.source.id + "," + reversed.mappedType.id + "," + reversed.constraintType.id + "," + inferenceConstraintType.id; + + if (reverseHomomorphicMappedCache.has(cacheKey)) { + return reverseHomomorphicMappedCache.get(cacheKey)!; + } + + const clone = createObjectType(ObjectFlags.ReverseMapped | ObjectFlags.Anonymous, /*symbol*/ undefined) as ReverseMappedType; + clone.source = reversed.source; + clone.mappedType = reversed.mappedType; + clone.constraintType = reversed.constraintType; + clone.inferenceConstraintType = inferenceConstraintType; + + reverseHomomorphicMappedCache.set(cacheKey, clone); + return clone; + } } function getDefaultTypeArgumentType(isInJavaScriptFile: boolean): Type { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cdba2e880afc3..b1013d83dc9cf 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6156,6 +6156,7 @@ export interface ReverseMappedSymbolLinks extends TransientSymbolLinks { propertyType: Type; mappedType: MappedType; constraintType: IndexType; + inferenceConstraintType?: Type; } /** @internal */ @@ -6788,6 +6789,7 @@ export interface ReverseMappedType extends ObjectType { source: Type; mappedType: MappedType; constraintType: IndexType; + inferenceConstraintType?: Type; } /** @internal */ diff --git a/tests/baselines/reference/reverseMappedDefaultInferenceToConstraint.symbols b/tests/baselines/reference/reverseMappedDefaultInferenceToConstraint.symbols new file mode 100644 index 0000000000000..3fbd5c76df940 --- /dev/null +++ b/tests/baselines/reference/reverseMappedDefaultInferenceToConstraint.symbols @@ -0,0 +1,154 @@ +//// [tests/cases/compiler/reverseMappedDefaultInferenceToConstraint.ts] //// + +=== reverseMappedDefaultInferenceToConstraint.ts === +// https://github.com/microsoft/TypeScript/issues/56241 + +interface ParameterizedObject { +>ParameterizedObject : Symbol(ParameterizedObject, Decl(reverseMappedDefaultInferenceToConstraint.ts, 0, 0)) + + type: string; +>type : Symbol(ParameterizedObject.type, Decl(reverseMappedDefaultInferenceToConstraint.ts, 2, 31)) + + params?: Record; +>params : Symbol(ParameterizedObject.params, Decl(reverseMappedDefaultInferenceToConstraint.ts, 3, 15)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +} + +declare function setup< +>setup : Symbol(setup, Decl(reverseMappedDefaultInferenceToConstraint.ts, 5, 1)) + + TContext, +>TContext : Symbol(TContext, Decl(reverseMappedDefaultInferenceToConstraint.ts, 7, 23)) + + TGuards extends Record, +>TGuards : Symbol(TGuards, Decl(reverseMappedDefaultInferenceToConstraint.ts, 8, 11)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>ParameterizedObject : Symbol(ParameterizedObject, Decl(reverseMappedDefaultInferenceToConstraint.ts, 0, 0)) + +>(_: { +>_ : Symbol(_, Decl(reverseMappedDefaultInferenceToConstraint.ts, 10, 2)) + + types: { +>types : Symbol(types, Decl(reverseMappedDefaultInferenceToConstraint.ts, 10, 6)) + + context: TContext; +>context : Symbol(context, Decl(reverseMappedDefaultInferenceToConstraint.ts, 11, 10)) +>TContext : Symbol(TContext, Decl(reverseMappedDefaultInferenceToConstraint.ts, 7, 23)) + + }; + guards: { +>guards : Symbol(guards, Decl(reverseMappedDefaultInferenceToConstraint.ts, 13, 4)) + + [K in keyof TGuards]: (context: TContext, params: TGuards[K]) => void; +>K : Symbol(K, Decl(reverseMappedDefaultInferenceToConstraint.ts, 15, 5)) +>TGuards : Symbol(TGuards, Decl(reverseMappedDefaultInferenceToConstraint.ts, 8, 11)) +>context : Symbol(context, Decl(reverseMappedDefaultInferenceToConstraint.ts, 15, 27)) +>TContext : Symbol(TContext, Decl(reverseMappedDefaultInferenceToConstraint.ts, 7, 23)) +>params : Symbol(params, Decl(reverseMappedDefaultInferenceToConstraint.ts, 15, 45)) +>TGuards : Symbol(TGuards, Decl(reverseMappedDefaultInferenceToConstraint.ts, 8, 11)) +>K : Symbol(K, Decl(reverseMappedDefaultInferenceToConstraint.ts, 15, 5)) + + }; +}): TGuards; +>TGuards : Symbol(TGuards, Decl(reverseMappedDefaultInferenceToConstraint.ts, 8, 11)) + +const result = setup({ +>result : Symbol(result, Decl(reverseMappedDefaultInferenceToConstraint.ts, 19, 5)) +>setup : Symbol(setup, Decl(reverseMappedDefaultInferenceToConstraint.ts, 5, 1)) + + types: { +>types : Symbol(types, Decl(reverseMappedDefaultInferenceToConstraint.ts, 19, 22)) + + context: { +>context : Symbol(context, Decl(reverseMappedDefaultInferenceToConstraint.ts, 20, 10)) + + count: 100, +>count : Symbol(count, Decl(reverseMappedDefaultInferenceToConstraint.ts, 21, 14)) + + }, + }, + guards: { +>guards : Symbol(guards, Decl(reverseMappedDefaultInferenceToConstraint.ts, 24, 4)) + + checkFoo: (_, { foo }: { foo: string }) => foo === "foo", +>checkFoo : Symbol(checkFoo, Decl(reverseMappedDefaultInferenceToConstraint.ts, 25, 11)) +>_ : Symbol(_, Decl(reverseMappedDefaultInferenceToConstraint.ts, 26, 15)) +>foo : Symbol(foo, Decl(reverseMappedDefaultInferenceToConstraint.ts, 26, 19)) +>foo : Symbol(foo, Decl(reverseMappedDefaultInferenceToConstraint.ts, 26, 28)) +>foo : Symbol(foo, Decl(reverseMappedDefaultInferenceToConstraint.ts, 26, 19)) + + alwaysTrue: (_) => true, +>alwaysTrue : Symbol(alwaysTrue, Decl(reverseMappedDefaultInferenceToConstraint.ts, 26, 61)) +>_ : Symbol(_, Decl(reverseMappedDefaultInferenceToConstraint.ts, 27, 17)) + + }, +}); + +declare function foo< +>foo : Symbol(foo, Decl(reverseMappedDefaultInferenceToConstraint.ts, 29, 3)) + + T extends Record, +>T : Symbol(T, Decl(reverseMappedDefaultInferenceToConstraint.ts, 31, 21)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) +>U : Symbol(U, Decl(reverseMappedDefaultInferenceToConstraint.ts, 32, 35)) + + U extends number | boolean, +>U : Symbol(U, Decl(reverseMappedDefaultInferenceToConstraint.ts, 32, 35)) + +>( + a: { +>a : Symbol(a, Decl(reverseMappedDefaultInferenceToConstraint.ts, 34, 2)) + + [K in keyof T]: (arg: T[K]) => void; +>K : Symbol(K, Decl(reverseMappedDefaultInferenceToConstraint.ts, 36, 5)) +>T : Symbol(T, Decl(reverseMappedDefaultInferenceToConstraint.ts, 31, 21)) +>arg : Symbol(arg, Decl(reverseMappedDefaultInferenceToConstraint.ts, 36, 21)) +>T : Symbol(T, Decl(reverseMappedDefaultInferenceToConstraint.ts, 31, 21)) +>K : Symbol(K, Decl(reverseMappedDefaultInferenceToConstraint.ts, 36, 5)) + + }, + b: U, +>b : Symbol(b, Decl(reverseMappedDefaultInferenceToConstraint.ts, 37, 4)) +>U : Symbol(U, Decl(reverseMappedDefaultInferenceToConstraint.ts, 32, 35)) + +): T; +>T : Symbol(T, Decl(reverseMappedDefaultInferenceToConstraint.ts, 31, 21)) + +declare const num: number; +>num : Symbol(num, Decl(reverseMappedDefaultInferenceToConstraint.ts, 41, 13)) + +const result1 = foo( +>result1 : Symbol(result1, Decl(reverseMappedDefaultInferenceToConstraint.ts, 43, 5)) +>foo : Symbol(foo, Decl(reverseMappedDefaultInferenceToConstraint.ts, 29, 3)) + { + a: (arg) => {}, +>a : Symbol(a, Decl(reverseMappedDefaultInferenceToConstraint.ts, 44, 3)) +>arg : Symbol(arg, Decl(reverseMappedDefaultInferenceToConstraint.ts, 45, 8)) + + b: () => {}, +>b : Symbol(b, Decl(reverseMappedDefaultInferenceToConstraint.ts, 45, 19)) + + }, + num, +>num : Symbol(num, Decl(reverseMappedDefaultInferenceToConstraint.ts, 41, 13)) + +); + +const result2 = foo( +>result2 : Symbol(result2, Decl(reverseMappedDefaultInferenceToConstraint.ts, 51, 5)) +>foo : Symbol(foo, Decl(reverseMappedDefaultInferenceToConstraint.ts, 29, 3)) + { + a: (arg: 100) => {}, +>a : Symbol(a, Decl(reverseMappedDefaultInferenceToConstraint.ts, 52, 3)) +>arg : Symbol(arg, Decl(reverseMappedDefaultInferenceToConstraint.ts, 53, 8)) + + b: () => {}, +>b : Symbol(b, Decl(reverseMappedDefaultInferenceToConstraint.ts, 53, 24)) + + }, + num, +>num : Symbol(num, Decl(reverseMappedDefaultInferenceToConstraint.ts, 41, 13)) + +); + diff --git a/tests/baselines/reference/reverseMappedDefaultInferenceToConstraint.types b/tests/baselines/reference/reverseMappedDefaultInferenceToConstraint.types new file mode 100644 index 0000000000000..2ae7518042b4b --- /dev/null +++ b/tests/baselines/reference/reverseMappedDefaultInferenceToConstraint.types @@ -0,0 +1,204 @@ +//// [tests/cases/compiler/reverseMappedDefaultInferenceToConstraint.ts] //// + +=== reverseMappedDefaultInferenceToConstraint.ts === +// https://github.com/microsoft/TypeScript/issues/56241 + +interface ParameterizedObject { + type: string; +>type : string +> : ^^^^^^ + + params?: Record; +>params : Record | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +} + +declare function setup< +>setup : >(_: { types: { context: TContext; }; guards: { [K in keyof TGuards]: (context: TContext, params: TGuards[K]) => void; }; }) => TGuards +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^^^^ + + TContext, + TGuards extends Record, +>(_: { +>_ : { types: { context: TContext; }; guards: { [K in keyof TGuards]: (context: TContext, params: TGuards[K]) => void; }; } +> : ^^^^^^^^^ ^^^^^^^^^^ ^^^ + + types: { +>types : { context: TContext; } +> : ^^^^^^^^^^^ ^^^ + + context: TContext; +>context : TContext +> : ^^^^^^^^ + + }; + guards: { +>guards : { [K in keyof TGuards]: (context: TContext, params: TGuards[K]) => void; } +> : ^^^ ^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^ ^^^^^ ^^^ + + [K in keyof TGuards]: (context: TContext, params: TGuards[K]) => void; +>context : TContext +> : ^^^^^^^^ +>params : TGuards[K] +> : ^^^^^^^^^^ + + }; +}): TGuards; + +const result = setup({ +>result : { checkFoo: { foo: string; }; alwaysTrue: Record | undefined; } +> : ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>setup({ types: { context: { count: 100, }, }, guards: { checkFoo: (_, { foo }: { foo: string }) => foo === "foo", alwaysTrue: (_) => true, },}) : { checkFoo: { foo: string; }; alwaysTrue: Record | undefined; } +> : ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>setup : >(_: { types: { context: TContext; }; guards: { [K in keyof TGuards]: (context: TContext, params: TGuards[K]) => void; }; }) => TGuards +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ types: { context: { count: 100, }, }, guards: { checkFoo: (_, { foo }: { foo: string }) => foo === "foo", alwaysTrue: (_) => true, },} : { types: { context: { count: number; }; }; guards: { checkFoo: (_: { count: number; }, { foo }: { foo: string; }) => boolean; alwaysTrue: (_: { count: number; }) => boolean; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + types: { +>types : { context: { count: number; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ context: { count: 100, }, } : { context: { count: number; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + context: { +>context : { count: number; } +> : ^^^^^^^^^^^^^^^^^^ +>{ count: 100, } : { count: number; } +> : ^^^^^^^^^^^^^^^^^^ + + count: 100, +>count : number +> : ^^^^^^ +>100 : 100 +> : ^^^ + + }, + }, + guards: { +>guards : { checkFoo: (_: { count: number; }, { foo }: { foo: string; }) => boolean; alwaysTrue: (_: { count: number; }) => boolean; } +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ checkFoo: (_, { foo }: { foo: string }) => foo === "foo", alwaysTrue: (_) => true, } : { checkFoo: (_: { count: number; }, { foo }: { foo: string; }) => boolean; alwaysTrue: (_: { count: number; }) => boolean; } +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + checkFoo: (_, { foo }: { foo: string }) => foo === "foo", +>checkFoo : (_: { count: number; }, { foo }: { foo: string; }) => boolean +> : ^ ^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^ +>(_, { foo }: { foo: string }) => foo === "foo" : (_: { count: number; }, { foo }: { foo: string; }) => boolean +> : ^ ^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^ +>_ : { count: number; } +> : ^^^^^^^^^^^^^^^^^^ +>foo : string +> : ^^^^^^ +>foo : string +> : ^^^^^^ +>foo === "foo" : boolean +> : ^^^^^^^ +>foo : string +> : ^^^^^^ +>"foo" : "foo" +> : ^^^^^ + + alwaysTrue: (_) => true, +>alwaysTrue : (_: { count: number; }) => boolean +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>(_) => true : (_: { count: number; }) => boolean +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>_ : { count: number; } +> : ^^^^^^^^^^^^^^^^^^ +>true : true +> : ^^^^ + + }, +}); + +declare function foo< +>foo : , U extends number | boolean>(a: { [K in keyof T]: (arg: T[K]) => void; }, b: U) => T +> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ + + T extends Record, + U extends number | boolean, +>( + a: { +>a : { [K in keyof T]: (arg: T[K]) => void; } +> : ^^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^ ^^^ + + [K in keyof T]: (arg: T[K]) => void; +>arg : T[K] +> : ^^^^ + + }, + b: U, +>b : U +> : ^ + +): T; + +declare const num: number; +>num : number +> : ^^^^^^ + +const result1 = foo( +>result1 : { a: number; b: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo( { a: (arg) => {}, b: () => {}, }, num,) : { a: number; b: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : , U extends number | boolean>(a: { [K in keyof T]: (arg: T[K]) => void; }, b: U) => T +> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ + { +>{ a: (arg) => {}, b: () => {}, } : { a: (arg: number) => void; b: () => void; } +> : ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + a: (arg) => {}, +>a : (arg: number) => void +> : ^ ^^^^^^^^^^^^^^^^^ +>(arg) => {} : (arg: number) => void +> : ^ ^^^^^^^^^^^^^^^^^ +>arg : number +> : ^^^^^^ + + b: () => {}, +>b : () => void +> : ^^^^^^^^^^ +>() => {} : () => void +> : ^^^^^^^^^^ + + }, + num, +>num : number +> : ^^^^^^ + +); + +const result2 = foo( +>result2 : { a: 100; b: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^ +>foo( { a: (arg: 100) => {}, b: () => {}, }, num,) : { a: 100; b: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^ +>foo : , U extends number | boolean>(a: { [K in keyof T]: (arg: T[K]) => void; }, b: U) => T +> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ + { +>{ a: (arg: 100) => {}, b: () => {}, } : { a: (arg: 100) => void; b: () => void; } +> : ^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + a: (arg: 100) => {}, +>a : (arg: 100) => void +> : ^ ^^ ^^^^^^^^^ +>(arg: 100) => {} : (arg: 100) => void +> : ^ ^^ ^^^^^^^^^ +>arg : 100 +> : ^^^ + + b: () => {}, +>b : () => void +> : ^^^^^^^^^^ +>() => {} : () => void +> : ^^^^^^^^^^ + + }, + num, +>num : number +> : ^^^^^^ + +); + diff --git a/tests/baselines/reference/reverseMappedFromContravariantPosition1.symbols b/tests/baselines/reference/reverseMappedFromContravariantPosition1.symbols new file mode 100644 index 0000000000000..7ee320b6666ec --- /dev/null +++ b/tests/baselines/reference/reverseMappedFromContravariantPosition1.symbols @@ -0,0 +1,44 @@ +//// [tests/cases/compiler/reverseMappedFromContravariantPosition1.ts] //// + +=== reverseMappedFromContravariantPosition1.ts === +declare function test1( +>test1 : Symbol(test1, Decl(reverseMappedFromContravariantPosition1.ts, 0, 0)) +>T : Symbol(T, Decl(reverseMappedFromContravariantPosition1.ts, 0, 23)) + + cb: (arg: { [K in keyof T]: { prop: T[K] } }) => void, +>cb : Symbol(cb, Decl(reverseMappedFromContravariantPosition1.ts, 0, 26)) +>arg : Symbol(arg, Decl(reverseMappedFromContravariantPosition1.ts, 1, 7)) +>K : Symbol(K, Decl(reverseMappedFromContravariantPosition1.ts, 1, 15)) +>T : Symbol(T, Decl(reverseMappedFromContravariantPosition1.ts, 0, 23)) +>prop : Symbol(prop, Decl(reverseMappedFromContravariantPosition1.ts, 1, 31)) +>T : Symbol(T, Decl(reverseMappedFromContravariantPosition1.ts, 0, 23)) +>K : Symbol(K, Decl(reverseMappedFromContravariantPosition1.ts, 1, 15)) + +): T; +>T : Symbol(T, Decl(reverseMappedFromContravariantPosition1.ts, 0, 23)) + +type Arg = { +>Arg : Symbol(Arg, Decl(reverseMappedFromContravariantPosition1.ts, 2, 5)) + + a: { +>a : Symbol(a, Decl(reverseMappedFromContravariantPosition1.ts, 4, 12)) + + prop: number; +>prop : Symbol(prop, Decl(reverseMappedFromContravariantPosition1.ts, 5, 6)) + + }; + b: { +>b : Symbol(b, Decl(reverseMappedFromContravariantPosition1.ts, 7, 4)) + + prop: boolean; +>prop : Symbol(prop, Decl(reverseMappedFromContravariantPosition1.ts, 8, 6)) + + }; +}; + +const result1 = test1((arg: Arg) => {}); +>result1 : Symbol(result1, Decl(reverseMappedFromContravariantPosition1.ts, 13, 5)) +>test1 : Symbol(test1, Decl(reverseMappedFromContravariantPosition1.ts, 0, 0)) +>arg : Symbol(arg, Decl(reverseMappedFromContravariantPosition1.ts, 13, 23)) +>Arg : Symbol(Arg, Decl(reverseMappedFromContravariantPosition1.ts, 2, 5)) + diff --git a/tests/baselines/reference/reverseMappedFromContravariantPosition1.types b/tests/baselines/reference/reverseMappedFromContravariantPosition1.types new file mode 100644 index 0000000000000..a3386a3c6585e --- /dev/null +++ b/tests/baselines/reference/reverseMappedFromContravariantPosition1.types @@ -0,0 +1,53 @@ +//// [tests/cases/compiler/reverseMappedFromContravariantPosition1.ts] //// + +=== reverseMappedFromContravariantPosition1.ts === +declare function test1( +>test1 : (cb: (arg: { [K in keyof T]: { prop: T[K]; }; }) => void) => T +> : ^ ^^ ^^ ^^^^^ + + cb: (arg: { [K in keyof T]: { prop: T[K] } }) => void, +>cb : (arg: { [K in keyof T]: { prop: T[K]; }; }) => void +> : ^ ^^ ^^^^^ +>arg : { [K in keyof T]: { prop: T[K]; }; } +> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^ +>prop : T[K] +> : ^^^^ + +): T; + +type Arg = { +>Arg : Arg +> : ^^^ + + a: { +>a : { prop: number; } +> : ^^^^^^^^ ^^^ + + prop: number; +>prop : number +> : ^^^^^^ + + }; + b: { +>b : { prop: boolean; } +> : ^^^^^^^^ ^^^ + + prop: boolean; +>prop : boolean +> : ^^^^^^^ + + }; +}; + +const result1 = test1((arg: Arg) => {}); +>result1 : { a: number; b: boolean; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>test1((arg: Arg) => {}) : { a: number; b: boolean; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>test1 : (cb: (arg: { [K in keyof T]: { prop: T[K]; }; }) => void) => T +> : ^ ^^ ^^ ^^^^^ +>(arg: Arg) => {} : (arg: Arg) => void +> : ^ ^^ ^^^^^^^^^ +>arg : Arg +> : ^^^ + diff --git a/tests/cases/compiler/reverseMappedDefaultInferenceToConstraint.ts b/tests/cases/compiler/reverseMappedDefaultInferenceToConstraint.ts new file mode 100644 index 0000000000000..ba934f6f4465e --- /dev/null +++ b/tests/cases/compiler/reverseMappedDefaultInferenceToConstraint.ts @@ -0,0 +1,61 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/56241 + +interface ParameterizedObject { + type: string; + params?: Record; +} + +declare function setup< + TContext, + TGuards extends Record, +>(_: { + types: { + context: TContext; + }; + guards: { + [K in keyof TGuards]: (context: TContext, params: TGuards[K]) => void; + }; +}): TGuards; + +const result = setup({ + types: { + context: { + count: 100, + }, + }, + guards: { + checkFoo: (_, { foo }: { foo: string }) => foo === "foo", + alwaysTrue: (_) => true, + }, +}); + +declare function foo< + T extends Record, + U extends number | boolean, +>( + a: { + [K in keyof T]: (arg: T[K]) => void; + }, + b: U, +): T; + +declare const num: number; + +const result1 = foo( + { + a: (arg) => {}, + b: () => {}, + }, + num, +); + +const result2 = foo( + { + a: (arg: 100) => {}, + b: () => {}, + }, + num, +); diff --git a/tests/cases/compiler/reverseMappedFromContravariantPosition1.ts b/tests/cases/compiler/reverseMappedFromContravariantPosition1.ts new file mode 100644 index 0000000000000..9fcd689f7799e --- /dev/null +++ b/tests/cases/compiler/reverseMappedFromContravariantPosition1.ts @@ -0,0 +1,17 @@ +// @strict: true +// @noEmit: true + +declare function test1( + cb: (arg: { [K in keyof T]: { prop: T[K] } }) => void, +): T; + +type Arg = { + a: { + prop: number; + }; + b: { + prop: boolean; + }; +}; + +const result1 = test1((arg: Arg) => {});