Skip to content

Commit 916aec6

Browse files
committed
Handle any in template holes, add assignability rules for template -> template relations
1 parent 2c51b77 commit 916aec6

File tree

6 files changed

+264
-8
lines changed

6 files changed

+264
-8
lines changed

src/compiler/checker.ts

+18-7
Original file line numberDiff line numberDiff line change
@@ -13518,7 +13518,7 @@ namespace ts {
1351813518
text += s;
1351913519
text += texts[i + 1];
1352013520
}
13521-
else if (isGenericIndexType(t) || templateConstraintType.types.indexOf(t) !== -1) {
13521+
else if (isGenericIndexType(t) || isPatternLiteralTypeHoleType(t)) {
1352213522
newTypes.push(t);
1352313523
newCasings.push(casings[i]);
1352413524
newTexts.push(text);
@@ -13784,8 +13784,12 @@ namespace ts {
1378413784
accessNode;
1378513785
}
1378613786

13787+
function isPatternLiteralTypeHoleType(type: Type) {
13788+
return templateConstraintType.types.indexOf(type) !== -1 || !!(type.flags & TypeFlags.Any);
13789+
}
13790+
1378713791
function isPatternLiteralType(type: Type) {
13788-
return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, t => templateConstraintType.types.indexOf(t) !== -1);
13792+
return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralTypeHoleType);
1378913793
}
1379013794

1379113795
function isGenericObjectType(type: Type): boolean {
@@ -17318,7 +17322,7 @@ namespace ts {
1731817322
if (isPatternLiteralType(target)) {
1731917323
// match all non-`string` segemnts
1732017324
const result = inferLiteralsFromTemplateLiteralType(source as StringLiteralType, target as TemplateLiteralType);
17321-
if (result && every(result, (r, i) => stringLiteralTypeParsesAsType(r, (target as TemplateLiteralType).types[i]))) {
17325+
if (result && every(result, (r, i) => isStringLiteralTypeValueParsableAsType(r, (target as TemplateLiteralType).types[i]))) {
1732217326
return Ternary.True;
1732317327
}
1732417328
}
@@ -17363,8 +17367,15 @@ namespace ts {
1736317367
}
1736417368
}
1736517369
else if (source.flags & TypeFlags.TemplateLiteral) {
17370+
if (target.flags & TypeFlags.TemplateLiteral &&
17371+
(source as TemplateLiteralType).texts.length === (target as TemplateLiteralType).texts.length &&
17372+
(source as TemplateLiteralType).types.length === (target as TemplateLiteralType).types.length &&
17373+
every((source as TemplateLiteralType).texts, (t, i) => t === (target as TemplateLiteralType).texts[i]) &&
17374+
every((instantiateType(source, makeFunctionTypeMapper(reportUnreliableMarkers)) as TemplateLiteralType).types, (t, i) => !!((target as TemplateLiteralType).types[i].flags & (TypeFlags.Any | TypeFlags.String)) || !!isRelatedTo(t, (target as TemplateLiteralType).types[i], /*reportErrors*/ false))) {
17375+
return Ternary.True;
17376+
}
1736617377
const constraint = getBaseConstraintOfType(source);
17367-
if (constraint && (result = isRelatedTo(constraint, target, reportErrors))) {
17378+
if (constraint && constraint !== source && (result = isRelatedTo(constraint, target, reportErrors))) {
1736817379
resetErrorInfo(saveErrorInfo);
1736917380
return result;
1737017381
}
@@ -19663,9 +19674,9 @@ namespace ts {
1966319674
return success && result === SyntaxKind.BigIntLiteral && scanner.getTextPos() === (s.length + 1) && !(flags & TokenFlags.ContainsSeparator);
1966419675
}
1966519676

19666-
function stringLiteralTypeParsesAsType(s: StringLiteralType, target: Type): boolean {
19677+
function isStringLiteralTypeValueParsableAsType(s: StringLiteralType, target: Type): boolean {
1966719678
if (target.flags & TypeFlags.Union) {
19668-
return !!forEachType(target, t => stringLiteralTypeParsesAsType(s, t));
19679+
return !!forEachType(target, t => isStringLiteralTypeValueParsableAsType(s, t));
1966919680
}
1967019681
switch (target) {
1967119682
case stringType: return true;
@@ -19677,7 +19688,7 @@ namespace ts {
1967719688
case falseType: return s.value === "false";
1967819689
case undefinedType: return s.value === "undefined";
1967919690
case nullType: return s.value === "null";
19680-
default: return false;
19691+
default: return !!(target.flags & TypeFlags.Any);
1968119692
}
1968219693
}
1968319694

tests/baselines/reference/templateLiteralTypesPatterns.errors.txt

+33-1
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(126,9): er
5252
tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(127,9): error TS2345: Argument of type '"-1.1e-10n"' is not assignable to parameter of type '`${bigint}`'.
5353
tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(128,9): error TS2345: Argument of type '"-1.1E-10n"' is not assignable to parameter of type '`${bigint}`'.
5454
tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(129,9): error TS2345: Argument of type '"1.1e-10n"' is not assignable to parameter of type '`${bigint}`'.
55+
tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(140,1): error TS2322: Type '`a${string}`' is not assignable to type '`a${number}`'.
56+
tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(141,1): error TS2322: Type '"bno"' is not assignable to type '`a${any}`'.
5557

5658

57-
==== tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts (54 errors) ====
59+
==== tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts (56 errors) ====
5860
type RequiresLeadingSlash = `/${string}`;
5961

6062
// ok
@@ -292,4 +294,34 @@ tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(129,9): er
292294
bigints("1.1e-10n");
293295
~~~~~~~~~~
294296
!!! error TS2345: Argument of type '"1.1e-10n"' is not assignable to parameter of type '`${bigint}`'.
297+
298+
type AStr = `a${string}`;
299+
type ANum = `a${number}`;
300+
type AAny = `a${any}`;
301+
302+
declare var str: AStr;
303+
declare var num: ANum;
304+
declare var anyish: AAny;
305+
306+
// not ok
307+
num = str;
308+
~~~
309+
!!! error TS2322: Type '`a${string}`' is not assignable to type '`a${number}`'.
310+
anyish = `bno`
311+
~~~~~~
312+
!!! error TS2322: Type '"bno"' is not assignable to type '`a${any}`'.
313+
314+
// ok
315+
str = num;
316+
anyish = str;
317+
str = anyish;
318+
anyish = num;
319+
num = anyish;
320+
anyish = `aok`
321+
322+
323+
// Validates variance isn't measured as strictly covariant
324+
type AGen<T extends string | number> = {field: `a${T}`};
325+
const shouldWork1: AGen<string> = null as any as AGen<"yes">;
326+
const shouldWork2: AGen<string> = null as any as AGen<number>;
295327

tests/baselines/reference/templateLiteralTypesPatterns.js

+38
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,32 @@ bigints("-1.1n");
128128
bigints("-1.1e-10n");
129129
bigints("-1.1E-10n");
130130
bigints("1.1e-10n");
131+
132+
type AStr = `a${string}`;
133+
type ANum = `a${number}`;
134+
type AAny = `a${any}`;
135+
136+
declare var str: AStr;
137+
declare var num: ANum;
138+
declare var anyish: AAny;
139+
140+
// not ok
141+
num = str;
142+
anyish = `bno`
143+
144+
// ok
145+
str = num;
146+
anyish = str;
147+
str = anyish;
148+
anyish = num;
149+
num = anyish;
150+
anyish = `aok`
151+
152+
153+
// Validates variance isn't measured as strictly covariant
154+
type AGen<T extends string | number> = {field: `a${T}`};
155+
const shouldWork1: AGen<string> = null as any as AGen<"yes">;
156+
const shouldWork2: AGen<string> = null as any as AGen<number>;
131157

132158

133159
//// [templateLiteralTypesPatterns.js]
@@ -235,3 +261,15 @@ bigints("-1.1n");
235261
bigints("-1.1e-10n");
236262
bigints("-1.1E-10n");
237263
bigints("1.1e-10n");
264+
// not ok
265+
num = str;
266+
anyish = "bno";
267+
// ok
268+
str = num;
269+
anyish = str;
270+
str = anyish;
271+
anyish = num;
272+
num = anyish;
273+
anyish = "aok";
274+
var shouldWork1 = null;
275+
var shouldWork2 = null;

tests/baselines/reference/templateLiteralTypesPatterns.symbols

+71
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,74 @@ bigints("-1.1E-10n");
305305
bigints("1.1e-10n");
306306
>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18))
307307

308+
type AStr = `a${string}`;
309+
>AStr : Symbol(AStr, Decl(templateLiteralTypesPatterns.ts, 128, 20))
310+
311+
type ANum = `a${number}`;
312+
>ANum : Symbol(ANum, Decl(templateLiteralTypesPatterns.ts, 130, 25))
313+
314+
type AAny = `a${any}`;
315+
>AAny : Symbol(AAny, Decl(templateLiteralTypesPatterns.ts, 131, 25))
316+
317+
declare var str: AStr;
318+
>str : Symbol(str, Decl(templateLiteralTypesPatterns.ts, 134, 11))
319+
>AStr : Symbol(AStr, Decl(templateLiteralTypesPatterns.ts, 128, 20))
320+
321+
declare var num: ANum;
322+
>num : Symbol(num, Decl(templateLiteralTypesPatterns.ts, 135, 11))
323+
>ANum : Symbol(ANum, Decl(templateLiteralTypesPatterns.ts, 130, 25))
324+
325+
declare var anyish: AAny;
326+
>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11))
327+
>AAny : Symbol(AAny, Decl(templateLiteralTypesPatterns.ts, 131, 25))
328+
329+
// not ok
330+
num = str;
331+
>num : Symbol(num, Decl(templateLiteralTypesPatterns.ts, 135, 11))
332+
>str : Symbol(str, Decl(templateLiteralTypesPatterns.ts, 134, 11))
333+
334+
anyish = `bno`
335+
>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11))
336+
337+
// ok
338+
str = num;
339+
>str : Symbol(str, Decl(templateLiteralTypesPatterns.ts, 134, 11))
340+
>num : Symbol(num, Decl(templateLiteralTypesPatterns.ts, 135, 11))
341+
342+
anyish = str;
343+
>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11))
344+
>str : Symbol(str, Decl(templateLiteralTypesPatterns.ts, 134, 11))
345+
346+
str = anyish;
347+
>str : Symbol(str, Decl(templateLiteralTypesPatterns.ts, 134, 11))
348+
>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11))
349+
350+
anyish = num;
351+
>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11))
352+
>num : Symbol(num, Decl(templateLiteralTypesPatterns.ts, 135, 11))
353+
354+
num = anyish;
355+
>num : Symbol(num, Decl(templateLiteralTypesPatterns.ts, 135, 11))
356+
>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11))
357+
358+
anyish = `aok`
359+
>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11))
360+
361+
362+
// Validates variance isn't measured as strictly covariant
363+
type AGen<T extends string | number> = {field: `a${T}`};
364+
>AGen : Symbol(AGen, Decl(templateLiteralTypesPatterns.ts, 148, 14))
365+
>T : Symbol(T, Decl(templateLiteralTypesPatterns.ts, 152, 10))
366+
>field : Symbol(field, Decl(templateLiteralTypesPatterns.ts, 152, 40))
367+
>T : Symbol(T, Decl(templateLiteralTypesPatterns.ts, 152, 10))
368+
369+
const shouldWork1: AGen<string> = null as any as AGen<"yes">;
370+
>shouldWork1 : Symbol(shouldWork1, Decl(templateLiteralTypesPatterns.ts, 153, 5))
371+
>AGen : Symbol(AGen, Decl(templateLiteralTypesPatterns.ts, 148, 14))
372+
>AGen : Symbol(AGen, Decl(templateLiteralTypesPatterns.ts, 148, 14))
373+
374+
const shouldWork2: AGen<string> = null as any as AGen<number>;
375+
>shouldWork2 : Symbol(shouldWork2, Decl(templateLiteralTypesPatterns.ts, 154, 5))
376+
>AGen : Symbol(AGen, Decl(templateLiteralTypesPatterns.ts, 148, 14))
377+
>AGen : Symbol(AGen, Decl(templateLiteralTypesPatterns.ts, 148, 14))
378+

tests/baselines/reference/templateLiteralTypesPatterns.types

+78
Original file line numberDiff line numberDiff line change
@@ -459,3 +459,81 @@ bigints("1.1e-10n");
459459
>bigints : (x: `${bigint}`) => void
460460
>"1.1e-10n" : "1.1e-10n"
461461

462+
type AStr = `a${string}`;
463+
>AStr : `a${string}`
464+
465+
type ANum = `a${number}`;
466+
>ANum : `a${number}`
467+
468+
type AAny = `a${any}`;
469+
>AAny : `a${any}`
470+
471+
declare var str: AStr;
472+
>str : `a${string}`
473+
474+
declare var num: ANum;
475+
>num : `a${number}`
476+
477+
declare var anyish: AAny;
478+
>anyish : `a${any}`
479+
480+
// not ok
481+
num = str;
482+
>num = str : `a${string}`
483+
>num : `a${number}`
484+
>str : `a${string}`
485+
486+
anyish = `bno`
487+
>anyish = `bno` : "bno"
488+
>anyish : `a${any}`
489+
>`bno` : "bno"
490+
491+
// ok
492+
str = num;
493+
>str = num : `a${number}`
494+
>str : `a${string}`
495+
>num : `a${number}`
496+
497+
anyish = str;
498+
>anyish = str : `a${string}`
499+
>anyish : `a${any}`
500+
>str : `a${string}`
501+
502+
str = anyish;
503+
>str = anyish : `a${any}`
504+
>str : `a${string}`
505+
>anyish : `a${any}`
506+
507+
anyish = num;
508+
>anyish = num : `a${number}`
509+
>anyish : `a${any}`
510+
>num : `a${number}`
511+
512+
num = anyish;
513+
>num = anyish : `a${any}`
514+
>num : `a${number}`
515+
>anyish : `a${any}`
516+
517+
anyish = `aok`
518+
>anyish = `aok` : "aok"
519+
>anyish : `a${any}`
520+
>`aok` : "aok"
521+
522+
523+
// Validates variance isn't measured as strictly covariant
524+
type AGen<T extends string | number> = {field: `a${T}`};
525+
>AGen : AGen<T>
526+
>field : `a${T}`
527+
528+
const shouldWork1: AGen<string> = null as any as AGen<"yes">;
529+
>shouldWork1 : AGen<string>
530+
>null as any as AGen<"yes"> : AGen<"yes">
531+
>null as any : any
532+
>null : null
533+
534+
const shouldWork2: AGen<string> = null as any as AGen<number>;
535+
>shouldWork2 : AGen<string>
536+
>null as any as AGen<number> : AGen<number>
537+
>null as any : any
538+
>null : null
539+

tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts

+26
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,29 @@ bigints("-1.1n");
128128
bigints("-1.1e-10n");
129129
bigints("-1.1E-10n");
130130
bigints("1.1e-10n");
131+
132+
type AStr = `a${string}`;
133+
type ANum = `a${number}`;
134+
type AAny = `a${any}`;
135+
136+
declare var str: AStr;
137+
declare var num: ANum;
138+
declare var anyish: AAny;
139+
140+
// not ok
141+
num = str;
142+
anyish = `bno`
143+
144+
// ok
145+
str = num;
146+
anyish = str;
147+
str = anyish;
148+
anyish = num;
149+
num = anyish;
150+
anyish = `aok`
151+
152+
153+
// Validates variance isn't measured as strictly covariant
154+
type AGen<T extends string | number> = {field: `a${T}`};
155+
const shouldWork1: AGen<string> = null as any as AGen<"yes">;
156+
const shouldWork2: AGen<string> = null as any as AGen<number>;

0 commit comments

Comments
 (0)