diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 79430adf777..519a9f4cf40 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11029,7 +11029,9 @@ namespace ts { const templateType = getTemplateTypeFromMappedType(target); const inference = createInferenceInfo(typeParameter); inferTypes([inference], sourceType, templateType); - return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType; + return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : + inference.contraCandidates ? getCommonSubtype(inference.contraCandidates) : + emptyObjectType; } function getUnmatchedProperty(source: Type, target: Type, requireOptionalProperties: boolean) { diff --git a/tests/baselines/reference/reverseMappedContravariantInference.js b/tests/baselines/reference/reverseMappedContravariantInference.js new file mode 100644 index 00000000000..7a3980e35aa --- /dev/null +++ b/tests/baselines/reference/reverseMappedContravariantInference.js @@ -0,0 +1,11 @@ +//// [reverseMappedContravariantInference.ts] +// Repro from #21273 + +declare function conforms(source: { [K in keyof T]: (val: T[K]) => boolean }): (value: T) => boolean; +conforms({ foo: (v: string) => false })({ foo: "hello" }); + + +//// [reverseMappedContravariantInference.js] +"use strict"; +// Repro from #21273 +conforms({ foo: function (v) { return false; } })({ foo: "hello" }); diff --git a/tests/baselines/reference/reverseMappedContravariantInference.symbols b/tests/baselines/reference/reverseMappedContravariantInference.symbols new file mode 100644 index 00000000000..6f8744a99fe --- /dev/null +++ b/tests/baselines/reference/reverseMappedContravariantInference.symbols @@ -0,0 +1,21 @@ +=== tests/cases/compiler/reverseMappedContravariantInference.ts === +// Repro from #21273 + +declare function conforms(source: { [K in keyof T]: (val: T[K]) => boolean }): (value: T) => boolean; +>conforms : Symbol(conforms, Decl(reverseMappedContravariantInference.ts, 0, 0)) +>T : Symbol(T, Decl(reverseMappedContravariantInference.ts, 2, 26)) +>source : Symbol(source, Decl(reverseMappedContravariantInference.ts, 2, 29)) +>K : Symbol(K, Decl(reverseMappedContravariantInference.ts, 2, 40)) +>T : Symbol(T, Decl(reverseMappedContravariantInference.ts, 2, 26)) +>val : Symbol(val, Decl(reverseMappedContravariantInference.ts, 2, 56)) +>T : Symbol(T, Decl(reverseMappedContravariantInference.ts, 2, 26)) +>K : Symbol(K, Decl(reverseMappedContravariantInference.ts, 2, 40)) +>value : Symbol(value, Decl(reverseMappedContravariantInference.ts, 2, 83)) +>T : Symbol(T, Decl(reverseMappedContravariantInference.ts, 2, 26)) + +conforms({ foo: (v: string) => false })({ foo: "hello" }); +>conforms : Symbol(conforms, Decl(reverseMappedContravariantInference.ts, 0, 0)) +>foo : Symbol(foo, Decl(reverseMappedContravariantInference.ts, 3, 10)) +>v : Symbol(v, Decl(reverseMappedContravariantInference.ts, 3, 17)) +>foo : Symbol(foo, Decl(reverseMappedContravariantInference.ts, 3, 41)) + diff --git a/tests/baselines/reference/reverseMappedContravariantInference.types b/tests/baselines/reference/reverseMappedContravariantInference.types new file mode 100644 index 00000000000..1decc716bbf --- /dev/null +++ b/tests/baselines/reference/reverseMappedContravariantInference.types @@ -0,0 +1,28 @@ +=== tests/cases/compiler/reverseMappedContravariantInference.ts === +// Repro from #21273 + +declare function conforms(source: { [K in keyof T]: (val: T[K]) => boolean }): (value: T) => boolean; +>conforms : (source: { [K in keyof T]: (val: T[K]) => boolean; }) => (value: T) => boolean +>T : T +>source : { [K in keyof T]: (val: T[K]) => boolean; } +>K : K +>T : T +>val : T[K] +>T : T +>K : K +>value : T +>T : T + +conforms({ foo: (v: string) => false })({ foo: "hello" }); +>conforms({ foo: (v: string) => false })({ foo: "hello" }) : boolean +>conforms({ foo: (v: string) => false }) : (value: { foo: any; }) => boolean +>conforms : (source: { [K in keyof T]: (val: T[K]) => boolean; }) => (value: T) => boolean +>{ foo: (v: string) => false } : { foo: (v: string) => boolean; } +>foo : (v: string) => boolean +>(v: string) => false : (v: string) => boolean +>v : string +>false : false +>{ foo: "hello" } : { foo: string; } +>foo : string +>"hello" : "hello" + diff --git a/tests/cases/compiler/reverseMappedContravariantInference.ts b/tests/cases/compiler/reverseMappedContravariantInference.ts new file mode 100644 index 00000000000..7b7492f6bad --- /dev/null +++ b/tests/cases/compiler/reverseMappedContravariantInference.ts @@ -0,0 +1,6 @@ +// @strict: true + +// Repro from #21273 + +declare function conforms(source: { [K in keyof T]: (val: T[K]) => boolean }): (value: T) => boolean; +conforms({ foo: (v: string) => false })({ foo: "hello" });