Skip to content

Commit 3db7f77

Browse files
committed
Fixed inference between type placeholders with non-string constraints in template literal types
1 parent 9866640 commit 3db7f77

File tree

5 files changed

+186
-2
lines changed

5 files changed

+186
-2
lines changed

src/compiler/checker.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25443,8 +25443,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2544325443
function inferTypesFromTemplateLiteralType(source: Type, target: TemplateLiteralType): Type[] | undefined {
2544425444
return source.flags & TypeFlags.StringLiteral ? inferFromLiteralPartsToTemplateLiteral([(source as StringLiteralType).value], emptyArray, target) :
2544525445
source.flags & TypeFlags.TemplateLiteral ?
25446-
arraysEqual((source as TemplateLiteralType).texts, target.texts) ? map((source as TemplateLiteralType).types, getStringLikeTypeForType) :
25447-
inferFromLiteralPartsToTemplateLiteral((source as TemplateLiteralType).texts, (source as TemplateLiteralType).types, target) :
25446+
arraysEqual((source as TemplateLiteralType).texts, target.texts) ? map((source as TemplateLiteralType).types, (s, i) => {
25447+
return isTypeAssignableTo(getBaseConstraintOrType(s), getBaseConstraintOrType(target.types[i])) ? s : getStringLikeTypeForType(s);
25448+
}) :
25449+
inferFromLiteralPartsToTemplateLiteral((source as TemplateLiteralType).texts, (source as TemplateLiteralType).types, target) :
2544825450
undefined;
2544925451
}
2545025452

@@ -26058,6 +26060,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2605826060
// allowed template literal placeholder types, infer from a literal type corresponding to the constraint.
2605926061
if (source.flags & TypeFlags.StringLiteral && target.flags & TypeFlags.TypeVariable) {
2606026062
const inferenceContext = getInferenceInfoForType(target);
26063+
// andarist here???
26064+
// declare function foo1<V extends string>(arg: `*${V}*`): V;
26065+
// declare const n: number
26066+
// let x5 = foo1(`*${n}*` as const);
26067+
26068+
// type Foo1<T> = T extends `*${infer U}*` ? U : never;
26069+
26070+
// type T05 = Foo1<`*${number}*`>;
26071+
26072+
// type T06 = Foo1<`*${bigint}*`>;
2606126073
const constraint = inferenceContext ? getBaseConstraintOfType(inferenceContext.typeParameter) : undefined;
2606226074
if (constraint && !isTypeAny(constraint)) {
2606326075
const constraintTypes = constraint.flags & TypeFlags.Union ? (constraint as UnionType).types : [constraint];
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
templateLiteralTypes7.ts(16,7): error TS2322: Type '<T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]' is not assignable to type 'G2'.
2+
Types of parameters 'x' and 'x' are incompatible.
3+
Type '`${T}`' is not assignable to type '"1" | "2" | "3"'.
4+
Type '"1" | "2" | "3" | "4"' is not assignable to type '"1" | "2" | "3"'.
5+
Type '"4"' is not assignable to type '"1" | "2" | "3"'.
6+
7+
8+
==== templateLiteralTypes7.ts (1 errors) ====
9+
// https://github.com/microsoft/TypeScript/issues/57807
10+
11+
interface NMap {
12+
1: 'A'
13+
2: 'B'
14+
3: 'C'
15+
4: 'D'
16+
}
17+
18+
declare const g: <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
19+
20+
type G1 = <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
21+
const g1: G1 = g; // ok
22+
23+
type G2 = <T extends 1 | 2 | 3 | 4>(x: `${T}`) => NMap[T]
24+
const g2: G2 = g; // error
25+
~~
26+
!!! error TS2322: Type '<T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]' is not assignable to type 'G2'.
27+
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
28+
!!! error TS2322: Type '`${T}`' is not assignable to type '"1" | "2" | "3"'.
29+
!!! error TS2322: Type '"1" | "2" | "3" | "4"' is not assignable to type '"1" | "2" | "3"'.
30+
!!! error TS2322: Type '"4"' is not assignable to type '"1" | "2" | "3"'.
31+
32+
type G3 = <T extends 1 | 2>(x: `${T}`) => NMap[T]
33+
const g3: G3 = g; // ok
34+
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//// [tests/cases/conformance/types/literal/templateLiteralTypes7.ts] ////
2+
3+
=== templateLiteralTypes7.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/57807
5+
6+
interface NMap {
7+
>NMap : Symbol(NMap, Decl(templateLiteralTypes7.ts, 0, 0))
8+
9+
1: 'A'
10+
>1 : Symbol(NMap[1], Decl(templateLiteralTypes7.ts, 2, 16))
11+
12+
2: 'B'
13+
>2 : Symbol(NMap[2], Decl(templateLiteralTypes7.ts, 3, 8))
14+
15+
3: 'C'
16+
>3 : Symbol(NMap[3], Decl(templateLiteralTypes7.ts, 4, 8))
17+
18+
4: 'D'
19+
>4 : Symbol(NMap[4], Decl(templateLiteralTypes7.ts, 5, 8))
20+
}
21+
22+
declare const g: <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
23+
>g : Symbol(g, Decl(templateLiteralTypes7.ts, 9, 13))
24+
>T : Symbol(T, Decl(templateLiteralTypes7.ts, 9, 18))
25+
>x : Symbol(x, Decl(templateLiteralTypes7.ts, 9, 39))
26+
>T : Symbol(T, Decl(templateLiteralTypes7.ts, 9, 18))
27+
>NMap : Symbol(NMap, Decl(templateLiteralTypes7.ts, 0, 0))
28+
>T : Symbol(T, Decl(templateLiteralTypes7.ts, 9, 18))
29+
30+
type G1 = <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
31+
>G1 : Symbol(G1, Decl(templateLiteralTypes7.ts, 9, 60))
32+
>T : Symbol(T, Decl(templateLiteralTypes7.ts, 11, 11))
33+
>x : Symbol(x, Decl(templateLiteralTypes7.ts, 11, 32))
34+
>T : Symbol(T, Decl(templateLiteralTypes7.ts, 11, 11))
35+
>NMap : Symbol(NMap, Decl(templateLiteralTypes7.ts, 0, 0))
36+
>T : Symbol(T, Decl(templateLiteralTypes7.ts, 11, 11))
37+
38+
const g1: G1 = g; // ok
39+
>g1 : Symbol(g1, Decl(templateLiteralTypes7.ts, 12, 5))
40+
>G1 : Symbol(G1, Decl(templateLiteralTypes7.ts, 9, 60))
41+
>g : Symbol(g, Decl(templateLiteralTypes7.ts, 9, 13))
42+
43+
type G2 = <T extends 1 | 2 | 3 | 4>(x: `${T}`) => NMap[T]
44+
>G2 : Symbol(G2, Decl(templateLiteralTypes7.ts, 12, 17))
45+
>T : Symbol(T, Decl(templateLiteralTypes7.ts, 14, 11))
46+
>x : Symbol(x, Decl(templateLiteralTypes7.ts, 14, 36))
47+
>T : Symbol(T, Decl(templateLiteralTypes7.ts, 14, 11))
48+
>NMap : Symbol(NMap, Decl(templateLiteralTypes7.ts, 0, 0))
49+
>T : Symbol(T, Decl(templateLiteralTypes7.ts, 14, 11))
50+
51+
const g2: G2 = g; // error
52+
>g2 : Symbol(g2, Decl(templateLiteralTypes7.ts, 15, 5))
53+
>G2 : Symbol(G2, Decl(templateLiteralTypes7.ts, 12, 17))
54+
>g : Symbol(g, Decl(templateLiteralTypes7.ts, 9, 13))
55+
56+
type G3 = <T extends 1 | 2>(x: `${T}`) => NMap[T]
57+
>G3 : Symbol(G3, Decl(templateLiteralTypes7.ts, 15, 17))
58+
>T : Symbol(T, Decl(templateLiteralTypes7.ts, 17, 11))
59+
>x : Symbol(x, Decl(templateLiteralTypes7.ts, 17, 28))
60+
>T : Symbol(T, Decl(templateLiteralTypes7.ts, 17, 11))
61+
>NMap : Symbol(NMap, Decl(templateLiteralTypes7.ts, 0, 0))
62+
>T : Symbol(T, Decl(templateLiteralTypes7.ts, 17, 11))
63+
64+
const g3: G3 = g; // ok
65+
>g3 : Symbol(g3, Decl(templateLiteralTypes7.ts, 18, 5))
66+
>G3 : Symbol(G3, Decl(templateLiteralTypes7.ts, 15, 17))
67+
>g : Symbol(g, Decl(templateLiteralTypes7.ts, 9, 13))
68+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//// [tests/cases/conformance/types/literal/templateLiteralTypes7.ts] ////
2+
3+
=== templateLiteralTypes7.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/57807
5+
6+
interface NMap {
7+
1: 'A'
8+
>1 : "A"
9+
10+
2: 'B'
11+
>2 : "B"
12+
13+
3: 'C'
14+
>3 : "C"
15+
16+
4: 'D'
17+
>4 : "D"
18+
}
19+
20+
declare const g: <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
21+
>g : <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
22+
>x : `${T}`
23+
24+
type G1 = <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
25+
>G1 : <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
26+
>x : `${T}`
27+
28+
const g1: G1 = g; // ok
29+
>g1 : G1
30+
>g : <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
31+
32+
type G2 = <T extends 1 | 2 | 3 | 4>(x: `${T}`) => NMap[T]
33+
>G2 : <T extends 1 | 2 | 3 | 4>(x: `${T}`) => NMap[T]
34+
>x : `${T}`
35+
36+
const g2: G2 = g; // error
37+
>g2 : G2
38+
>g : <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
39+
40+
type G3 = <T extends 1 | 2>(x: `${T}`) => NMap[T]
41+
>G3 : <T extends 1 | 2>(x: `${T}`) => NMap[T]
42+
>x : `${T}`
43+
44+
const g3: G3 = g; // ok
45+
>g3 : G3
46+
>g : <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
47+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// @strict: true
2+
// @target: esnext
3+
// @noEmit: true
4+
5+
// https://github.com/microsoft/TypeScript/issues/57807
6+
7+
interface NMap {
8+
1: 'A'
9+
2: 'B'
10+
3: 'C'
11+
4: 'D'
12+
}
13+
14+
declare const g: <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
15+
16+
type G1 = <T extends 1 | 2 | 3>(x: `${T}`) => NMap[T]
17+
const g1: G1 = g; // ok
18+
19+
type G2 = <T extends 1 | 2 | 3 | 4>(x: `${T}`) => NMap[T]
20+
const g2: G2 = g; // error
21+
22+
type G3 = <T extends 1 | 2>(x: `${T}`) => NMap[T]
23+
const g3: G3 = g; // ok

0 commit comments

Comments
 (0)