From 74457dfa8e67cd1b29dda793422d8a19aa68051e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 16 Mar 2024 21:04:03 +0100 Subject: [PATCH] Fixed inference between type placeholders with non-string constraints in template literal types --- src/compiler/checker.ts | 4 +- .../templateLiteralTypes7.errors.txt | 34 ++++++++++ .../reference/templateLiteralTypes7.symbols | 68 +++++++++++++++++++ .../reference/templateLiteralTypes7.types | 47 +++++++++++++ .../types/literal/templateLiteralTypes7.ts | 23 +++++++ 5 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/templateLiteralTypes7.errors.txt create mode 100644 tests/baselines/reference/templateLiteralTypes7.symbols create mode 100644 tests/baselines/reference/templateLiteralTypes7.types create mode 100644 tests/cases/conformance/types/literal/templateLiteralTypes7.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c8eefa77783f3..29933604d77eb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25443,7 +25443,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function inferTypesFromTemplateLiteralType(source: Type, target: TemplateLiteralType): Type[] | undefined { return source.flags & TypeFlags.StringLiteral ? inferFromLiteralPartsToTemplateLiteral([(source as StringLiteralType).value], emptyArray, target) : source.flags & TypeFlags.TemplateLiteral ? - arraysEqual((source as TemplateLiteralType).texts, target.texts) ? map((source as TemplateLiteralType).types, getStringLikeTypeForType) : + arraysEqual((source as TemplateLiteralType).texts, target.texts) ? map((source as TemplateLiteralType).types, (s, i) => { + return isTypeAssignableTo(getBaseConstraintOrType(s), getBaseConstraintOrType(target.types[i])) ? s : getStringLikeTypeForType(s); + }) : inferFromLiteralPartsToTemplateLiteral((source as TemplateLiteralType).texts, (source as TemplateLiteralType).types, target) : undefined; } diff --git a/tests/baselines/reference/templateLiteralTypes7.errors.txt b/tests/baselines/reference/templateLiteralTypes7.errors.txt new file mode 100644 index 0000000000000..0d5964cb2e330 --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypes7.errors.txt @@ -0,0 +1,34 @@ +templateLiteralTypes7.ts(16,7): error TS2322: Type '(x: `${T}`) => NMap[T]' is not assignable to type 'G2'. + Types of parameters 'x' and 'x' are incompatible. + Type '`${T}`' is not assignable to type '"1" | "2" | "3"'. + Type '"1" | "2" | "3" | "4"' is not assignable to type '"1" | "2" | "3"'. + Type '"4"' is not assignable to type '"1" | "2" | "3"'. + + +==== templateLiteralTypes7.ts (1 errors) ==== + // https://github.com/microsoft/TypeScript/issues/57807 + + interface NMap { + 1: 'A' + 2: 'B' + 3: 'C' + 4: 'D' + } + + declare const g: (x: `${T}`) => NMap[T] + + type G1 = (x: `${T}`) => NMap[T] + const g1: G1 = g; // ok + + type G2 = (x: `${T}`) => NMap[T] + const g2: G2 = g; // error + ~~ +!!! error TS2322: Type '(x: `${T}`) => NMap[T]' is not assignable to type 'G2'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type '`${T}`' is not assignable to type '"1" | "2" | "3"'. +!!! error TS2322: Type '"1" | "2" | "3" | "4"' is not assignable to type '"1" | "2" | "3"'. +!!! error TS2322: Type '"4"' is not assignable to type '"1" | "2" | "3"'. + + type G3 = (x: `${T}`) => NMap[T] + const g3: G3 = g; // ok + \ No newline at end of file diff --git a/tests/baselines/reference/templateLiteralTypes7.symbols b/tests/baselines/reference/templateLiteralTypes7.symbols new file mode 100644 index 0000000000000..9770ea8dbb9e4 --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypes7.symbols @@ -0,0 +1,68 @@ +//// [tests/cases/conformance/types/literal/templateLiteralTypes7.ts] //// + +=== templateLiteralTypes7.ts === +// https://github.com/microsoft/TypeScript/issues/57807 + +interface NMap { +>NMap : Symbol(NMap, Decl(templateLiteralTypes7.ts, 0, 0)) + + 1: 'A' +>1 : Symbol(NMap[1], Decl(templateLiteralTypes7.ts, 2, 16)) + + 2: 'B' +>2 : Symbol(NMap[2], Decl(templateLiteralTypes7.ts, 3, 8)) + + 3: 'C' +>3 : Symbol(NMap[3], Decl(templateLiteralTypes7.ts, 4, 8)) + + 4: 'D' +>4 : Symbol(NMap[4], Decl(templateLiteralTypes7.ts, 5, 8)) +} + +declare const g: (x: `${T}`) => NMap[T] +>g : Symbol(g, Decl(templateLiteralTypes7.ts, 9, 13)) +>T : Symbol(T, Decl(templateLiteralTypes7.ts, 9, 18)) +>x : Symbol(x, Decl(templateLiteralTypes7.ts, 9, 39)) +>T : Symbol(T, Decl(templateLiteralTypes7.ts, 9, 18)) +>NMap : Symbol(NMap, Decl(templateLiteralTypes7.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes7.ts, 9, 18)) + +type G1 = (x: `${T}`) => NMap[T] +>G1 : Symbol(G1, Decl(templateLiteralTypes7.ts, 9, 60)) +>T : Symbol(T, Decl(templateLiteralTypes7.ts, 11, 11)) +>x : Symbol(x, Decl(templateLiteralTypes7.ts, 11, 32)) +>T : Symbol(T, Decl(templateLiteralTypes7.ts, 11, 11)) +>NMap : Symbol(NMap, Decl(templateLiteralTypes7.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes7.ts, 11, 11)) + +const g1: G1 = g; // ok +>g1 : Symbol(g1, Decl(templateLiteralTypes7.ts, 12, 5)) +>G1 : Symbol(G1, Decl(templateLiteralTypes7.ts, 9, 60)) +>g : Symbol(g, Decl(templateLiteralTypes7.ts, 9, 13)) + +type G2 = (x: `${T}`) => NMap[T] +>G2 : Symbol(G2, Decl(templateLiteralTypes7.ts, 12, 17)) +>T : Symbol(T, Decl(templateLiteralTypes7.ts, 14, 11)) +>x : Symbol(x, Decl(templateLiteralTypes7.ts, 14, 36)) +>T : Symbol(T, Decl(templateLiteralTypes7.ts, 14, 11)) +>NMap : Symbol(NMap, Decl(templateLiteralTypes7.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes7.ts, 14, 11)) + +const g2: G2 = g; // error +>g2 : Symbol(g2, Decl(templateLiteralTypes7.ts, 15, 5)) +>G2 : Symbol(G2, Decl(templateLiteralTypes7.ts, 12, 17)) +>g : Symbol(g, Decl(templateLiteralTypes7.ts, 9, 13)) + +type G3 = (x: `${T}`) => NMap[T] +>G3 : Symbol(G3, Decl(templateLiteralTypes7.ts, 15, 17)) +>T : Symbol(T, Decl(templateLiteralTypes7.ts, 17, 11)) +>x : Symbol(x, Decl(templateLiteralTypes7.ts, 17, 28)) +>T : Symbol(T, Decl(templateLiteralTypes7.ts, 17, 11)) +>NMap : Symbol(NMap, Decl(templateLiteralTypes7.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes7.ts, 17, 11)) + +const g3: G3 = g; // ok +>g3 : Symbol(g3, Decl(templateLiteralTypes7.ts, 18, 5)) +>G3 : Symbol(G3, Decl(templateLiteralTypes7.ts, 15, 17)) +>g : Symbol(g, Decl(templateLiteralTypes7.ts, 9, 13)) + diff --git a/tests/baselines/reference/templateLiteralTypes7.types b/tests/baselines/reference/templateLiteralTypes7.types new file mode 100644 index 0000000000000..690848be37473 --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypes7.types @@ -0,0 +1,47 @@ +//// [tests/cases/conformance/types/literal/templateLiteralTypes7.ts] //// + +=== templateLiteralTypes7.ts === +// https://github.com/microsoft/TypeScript/issues/57807 + +interface NMap { + 1: 'A' +>1 : "A" + + 2: 'B' +>2 : "B" + + 3: 'C' +>3 : "C" + + 4: 'D' +>4 : "D" +} + +declare const g: (x: `${T}`) => NMap[T] +>g : (x: `${T}`) => NMap[T] +>x : `${T}` + +type G1 = (x: `${T}`) => NMap[T] +>G1 : (x: `${T}`) => NMap[T] +>x : `${T}` + +const g1: G1 = g; // ok +>g1 : G1 +>g : (x: `${T}`) => NMap[T] + +type G2 = (x: `${T}`) => NMap[T] +>G2 : (x: `${T}`) => NMap[T] +>x : `${T}` + +const g2: G2 = g; // error +>g2 : G2 +>g : (x: `${T}`) => NMap[T] + +type G3 = (x: `${T}`) => NMap[T] +>G3 : (x: `${T}`) => NMap[T] +>x : `${T}` + +const g3: G3 = g; // ok +>g3 : G3 +>g : (x: `${T}`) => NMap[T] + diff --git a/tests/cases/conformance/types/literal/templateLiteralTypes7.ts b/tests/cases/conformance/types/literal/templateLiteralTypes7.ts new file mode 100644 index 0000000000000..2f06f286d2aa7 --- /dev/null +++ b/tests/cases/conformance/types/literal/templateLiteralTypes7.ts @@ -0,0 +1,23 @@ +// @strict: true +// @target: esnext +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/57807 + +interface NMap { + 1: 'A' + 2: 'B' + 3: 'C' + 4: 'D' +} + +declare const g: (x: `${T}`) => NMap[T] + +type G1 = (x: `${T}`) => NMap[T] +const g1: G1 = g; // ok + +type G2 = (x: `${T}`) => NMap[T] +const g2: G2 = g; // error + +type G3 = (x: `${T}`) => NMap[T] +const g3: G3 = g; // ok