Skip to content

Commit eee799f

Browse files
authored
Properly check types in template literal placeholders (microsoft#40498)
* Properly check types in template literal placeholders * Add regression test * Update test * Accept new baselines
1 parent ea51fab commit eee799f

File tree

6 files changed

+216
-189
lines changed

6 files changed

+216
-189
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31548,16 +31548,16 @@ namespace ts {
3154831548
registerForUnusedIdentifiersCheck(node);
3154931549
}
3155031550

31551-
function checkTemplateType(node: TemplateLiteralTypeNode) {
31552-
forEachChild(node, checkSourceElement);
31553-
getTypeFromTypeNode(node);
31551+
function checkTemplateLiteralType(node: TemplateLiteralTypeNode) {
3155431552
for (const span of node.templateSpans) {
31553+
checkSourceElement(span.type);
3155531554
const type = getTypeFromTypeNode(span.type);
3155631555
checkTypeAssignableTo(type, templateConstraintType, span.type);
3155731556
if (!everyType(type, t => !!(t.flags & TypeFlags.Literal) || isGenericIndexType(t))) {
3155831557
error(span.type, Diagnostics.Template_literal_type_argument_0_is_not_literal_type_or_a_generic_type, typeToString(type));
3155931558
}
3156031559
}
31560+
getTypeFromTypeNode(node);
3156131561
}
3156231562

3156331563
function checkImportType(node: ImportTypeNode) {
@@ -36198,7 +36198,7 @@ namespace ts {
3619836198
case SyntaxKind.InferType:
3619936199
return checkInferType(<InferTypeNode>node);
3620036200
case SyntaxKind.TemplateLiteralType:
36201-
return checkTemplateType(<TemplateLiteralTypeNode>node);
36201+
return checkTemplateLiteralType(<TemplateLiteralTypeNode>node);
3620236202
case SyntaxKind.ImportType:
3620336203
return checkImportType(<ImportTypeNode>node);
3620436204
case SyntaxKind.NamedTupleMember:

tests/baselines/reference/templateLiteralTypes1.errors.txt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ tests/cases/conformance/types/literal/templateLiteralTypes1.ts(39,5): error TS23
44
'A' is assignable to the constraint of type 'B', but 'B' could be instantiated with a different subtype of constraint 'string'.
55
Type 'string' is not assignable to type 'B'.
66
'string' is assignable to the constraint of type 'B', but 'B' could be instantiated with a different subtype of constraint 'string'.
7-
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(184,16): error TS2590: Expression produces a union type that is too complex to represent.
7+
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(165,15): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
88
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(188,16): error TS2590: Expression produces a union type that is too complex to represent.
99
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(192,16): error TS2590: Expression produces a union type that is too complex to represent.
10+
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(196,16): error TS2590: Expression produces a union type that is too complex to represent.
1011

1112

12-
==== tests/cases/conformance/types/literal/templateLiteralTypes1.ts (5 errors) ====
13+
==== tests/cases/conformance/types/literal/templateLiteralTypes1.ts (6 errors) ====
1314
// Template types example from #12754
1415

1516
const createScopedActionType = <S extends string>(scope: S) => <T extends string>(type: T) => `${scope}/${type}` as `${S}/${T}`;
@@ -61,10 +62,10 @@ tests/cases/conformance/types/literal/templateLiteralTypes1.ts(192,16): error TS
6162

6263
// String transformations using recursive conditional types
6364

64-
type Join<T extends (string | number | boolean | bigint)[], D extends string> =
65+
type Join<T extends unknown[], D extends string> =
6566
T extends [] ? '' :
66-
T extends [unknown] ? `${T[0]}` :
67-
T extends [unknown, ...infer U] ? `${T[0]}${D}${Join<U, D>}` :
67+
T extends [string | number | boolean | bigint] ? `${T[0]}` :
68+
T extends [string | number | boolean | bigint, ...infer U] ? `${T[0]}${D}${Join<U, D>}` :
6869
string;
6970

7071
type TJ1 = Join<[1, 2, 3, 4], '.'>
@@ -180,6 +181,12 @@ tests/cases/conformance/types/literal/templateLiteralTypes1.ts(192,16): error TS
180181
type S1<T> = T extends `foo${infer U}bar` ? S2<U> : never;
181182
type S2<S extends string> = S;
182183

184+
// Check that infer T declarations are validated
185+
186+
type TV1 = `${infer X}`;
187+
~~~~~~~
188+
!!! error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
189+
183190
// Batched single character inferences for lower recursion depth
184191

185192
type Chars<S extends string> =

tests/baselines/reference/templateLiteralTypes1.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ function fa2<T, U extends T, A extends string, B extends A>(x: { [P in B as `p_$
4242

4343
// String transformations using recursive conditional types
4444

45-
type Join<T extends (string | number | boolean | bigint)[], D extends string> =
45+
type Join<T extends unknown[], D extends string> =
4646
T extends [] ? '' :
47-
T extends [unknown] ? `${T[0]}` :
48-
T extends [unknown, ...infer U] ? `${T[0]}${D}${Join<U, D>}` :
47+
T extends [string | number | boolean | bigint] ? `${T[0]}` :
48+
T extends [string | number | boolean | bigint, ...infer U] ? `${T[0]}${D}${Join<U, D>}` :
4949
string;
5050
5151
type TJ1 = Join<[1, 2, 3, 4], '.'>
@@ -161,6 +161,10 @@ getPropValue(obj, s); // unknown
161161
type S1<T> = T extends `foo${infer U}bar` ? S2<U> : never;
162162
type S2<S extends string> = S;
163163

164+
// Check that infer T declarations are validated
165+
166+
type TV1 = `${infer X}`;
167+
164168
// Batched single character inferences for lower recursion depth
165169

166170
type Chars<S extends string> =
@@ -246,7 +250,7 @@ declare function fa2<T, U extends T, A extends string, B extends A>(x: {
246250
}, y: {
247251
[Q in A as `p_${Q}`]: U;
248252
}): void;
249-
declare type Join<T extends (string | number | boolean | bigint)[], D extends string> = T extends [] ? '' : T extends [unknown] ? `${T[0]}` : T extends [unknown, ...infer U] ? `${T[0]}${D}${Join<U, D>}` : string;
253+
declare type Join<T extends unknown[], D extends string> = T extends [] ? '' : T extends [string | number | boolean | bigint] ? `${T[0]}` : T extends [string | number | boolean | bigint, ...infer U] ? `${T[0]}${D}${Join<U, D>}` : string;
250254
declare type TJ1 = Join<[1, 2, 3, 4], '.'>;
251255
declare type TJ2 = Join<['foo', 'bar', 'baz'], '-'>;
252256
declare type TJ3 = Join<[], '.'>;
@@ -322,6 +326,7 @@ declare const obj: {
322326
};
323327
declare type S1<T> = T extends `foo${infer U}bar` ? S2<U> : never;
324328
declare type S2<S extends string> = S;
329+
declare type TV1 = `${infer X}`;
325330
declare type Chars<S extends string> = string extends S ? string[] : S extends `${infer C0}${infer C1}${infer C2}${infer C3}${infer C4}${infer C5}${infer C6}${infer C7}${infer C8}${infer C9}${infer R}` ? [C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, ...Chars<R>] : S extends `${infer C}${infer R}` ? [C, ...Chars<R>] : S extends '' ? [] : never;
326331
declare type L1 = Chars<'FooBarBazThisIsALongerString'>;
327332
declare type A = any;

0 commit comments

Comments
 (0)