Support relations and inference between template literal types (#43361)

* Support assignment and inference between template literal types

* Add tests

* Accept new baselines

* Add comments
This commit is contained in:
Anders Hejlsberg
2021-03-28 13:11:56 -07:00
committed by GitHub
parent 451089e8ef
commit 4774666f23
6 changed files with 1249 additions and 45 deletions

View File

@@ -14295,9 +14295,7 @@ namespace ts {
return type.flags & TypeFlags.StringLiteral ? (<StringLiteralType>type).value :
type.flags & TypeFlags.NumberLiteral ? "" + (<NumberLiteralType>type).value :
type.flags & TypeFlags.BigIntLiteral ? pseudoBigIntToString((<BigIntLiteralType>type).value) :
type.flags & TypeFlags.BooleanLiteral ? (<IntrinsicType>type).intrinsicName :
type.flags & TypeFlags.Null ? "null" :
type.flags & TypeFlags.Undefined ? "undefined" :
type.flags & (TypeFlags.BooleanLiteral | TypeFlags.Nullable) ? (<IntrinsicType>type).intrinsicName :
undefined;
}
@@ -14577,7 +14575,7 @@ namespace ts {
}
function isPatternLiteralPlaceholderType(type: Type) {
return templateConstraintType.types.indexOf(type) !== -1 || !!(type.flags & TypeFlags.Any);
return !!(type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt));
}
function isPatternLiteralType(type: Type) {
@@ -18275,13 +18273,10 @@ namespace ts {
return localResult;
}
}
else if (target.flags & TypeFlags.TemplateLiteral && source.flags & TypeFlags.StringLiteral) {
if (isPatternLiteralType(target)) {
// match all non-`string` segments
const result = inferLiteralsFromTemplateLiteralType(source as StringLiteralType, target as TemplateLiteralType);
if (result && every(result, (r, i) => isStringLiteralTypeValueParsableAsType(r, (target as TemplateLiteralType).types[i]))) {
return Ternary.True;
}
else if (target.flags & TypeFlags.TemplateLiteral) {
const result = inferTypesFromTemplateLiteralType(source, target as TemplateLiteralType);
if (result && every(result, (r, i) => isValidTypeForTemplateLiteralPlaceholder(r, (target as TemplateLiteralType).types[i]))) {
return Ternary.True;
}
}
@@ -20688,43 +20683,108 @@ namespace ts {
return success && result === SyntaxKind.BigIntLiteral && scanner.getTextPos() === (s.length + 1) && !(flags & TokenFlags.ContainsSeparator);
}
function isStringLiteralTypeValueParsableAsType(s: StringLiteralType, target: Type): boolean {
if (target.flags & TypeFlags.Union) {
return someType(target, t => isStringLiteralTypeValueParsableAsType(s, t));
function isValidTypeForTemplateLiteralPlaceholder(source: Type, target: Type): boolean {
if (source === target || target.flags & (TypeFlags.Any | TypeFlags.String)) {
return true;
}
switch (target) {
case stringType: return true;
case numberType: return s.value !== "" && isFinite(+(s.value));
case bigintType: return s.value !== "" && isValidBigIntString(s.value);
// the next 4 should be handled in `getTemplateLiteralType`, as they are all exactly one value, but are here for completeness, just in case
// this function is ever used on types which don't come from template literal holes
case trueType: return s.value === "true";
case falseType: return s.value === "false";
case undefinedType: return s.value === "undefined";
case nullType: return s.value === "null";
default: return !!(target.flags & TypeFlags.Any);
if (source.flags & TypeFlags.StringLiteral) {
const value = (<StringLiteralType>source).value;
return !!(target.flags & TypeFlags.Number && value !== "" && isFinite(+value) ||
target.flags & TypeFlags.BigInt && value !== "" && isValidBigIntString(value) ||
target.flags & (TypeFlags.BooleanLiteral | TypeFlags.Nullable) && value === (<IntrinsicType>target).intrinsicName);
}
if (source.flags & TypeFlags.TemplateLiteral) {
const texts = (<TemplateLiteralType>source).texts;
return texts.length === 2 && texts[0] === "" && texts[1] === "" && isTypeAssignableTo((<TemplateLiteralType>source).types[0], target);
}
return isTypeAssignableTo(source, target);
}
function inferLiteralsFromTemplateLiteralType(source: StringLiteralType, target: TemplateLiteralType): StringLiteralType[] | undefined {
const value = source.value;
const texts = target.texts;
const lastIndex = texts.length - 1;
const startText = texts[0];
const endText = texts[lastIndex];
if (!(value.startsWith(startText) && value.slice(startText.length).endsWith(endText))) return undefined;
const matches = [];
const str = value.slice(startText.length, value.length - endText.length);
let pos = 0;
for (let i = 1; i < lastIndex; i++) {
const delim = texts[i];
const delimPos = delim.length > 0 ? str.indexOf(delim, pos) : pos < str.length ? pos + 1 : -1;
if (delimPos < 0) return undefined;
matches.push(getLiteralType(str.slice(pos, delimPos)));
pos = delimPos + delim.length;
function inferTypesFromTemplateLiteralType(source: Type, target: TemplateLiteralType): Type[] | undefined {
return source.flags & TypeFlags.StringLiteral ? inferFromLiteralPartsToTemplateLiteral([(<StringLiteralType>source).value], emptyArray, target) :
source.flags & TypeFlags.TemplateLiteral ?
arraysEqual((<TemplateLiteralType>source).texts, target.texts) ? map((<TemplateLiteralType>source).types, getStringLikeTypeForType) :
inferFromLiteralPartsToTemplateLiteral((<TemplateLiteralType>source).texts, (<TemplateLiteralType>source).types, target) :
undefined;
}
function getStringLikeTypeForType(type: Type) {
return type.flags & (TypeFlags.Any | TypeFlags.StringLike) ? type : getTemplateLiteralType(["", ""], [type]);
}
// This function infers from the text parts and type parts of a source literal to a target template literal. The number
// of text parts is always one more than the number of type parts, and a source string literal is treated as a source
// with one text part and zero type parts. The function returns an array of inferred string or template literal types
// corresponding to the placeholders in the target template literal, or undefined if the source doesn't match the target.
//
// We first check that the starting source text part matches the starting target text part, and that the ending source
// text part ends matches the ending target text part. We then iterate through the remaining target text parts, finding
// a match for each in the source and inferring string or template literal types created from the segments of the source
// that occur between the matches. During this iteration, seg holds the index of the current text part in the sourceTexts
// array and pos holds the current character position in the current text part.
//
// Consider inference from type `<<${string}>.<${number}-${number}>>` to type `<${string}.${string}>`, i.e.
// sourceTexts = ['<<', '>.<', '-', '>>']
// sourceTypes = [string, number, number]
// target.texts = ['<', '.', '>']
// We first match '<' in the target to the start of '<<' in the source and '>' in the target to the end of '>>' in
// the source. The first match for the '.' in target occurs at character 1 in the source text part at index 1, and thus
// the first inference is the template literal type `<${string}>`. The remainder of the source makes up the second
// inference, the template literal type `<${number}-${number}>`.
function inferFromLiteralPartsToTemplateLiteral(sourceTexts: readonly string[], sourceTypes: readonly Type[], target: TemplateLiteralType): Type[] | undefined {
const lastSourceIndex = sourceTexts.length - 1;
const sourceStartText = sourceTexts[0];
const sourceEndText = sourceTexts[lastSourceIndex];
const targetTexts = target.texts;
const lastTargetIndex = targetTexts.length - 1;
const targetStartText = targetTexts[0];
const targetEndText = targetTexts[lastTargetIndex];
if (lastSourceIndex === 0 && sourceStartText.length < targetStartText.length + targetEndText.length ||
!sourceStartText.startsWith(targetStartText) || !sourceEndText.endsWith(targetEndText)) return undefined;
const remainingEndText = sourceEndText.slice(0, sourceEndText.length - targetEndText.length);
const matches: Type[] = [];
let seg = 0;
let pos = targetStartText.length;
for (let i = 1; i < lastTargetIndex; i++) {
const delim = targetTexts[i];
if (delim.length > 0) {
let s = seg;
let p = pos;
while (true) {
p = getSourceText(s).indexOf(delim, p);
if (p >= 0) break;
s++;
if (s === sourceTexts.length) return undefined;
p = 0;
}
addMatch(s, p);
pos += delim.length;
}
else if (pos < getSourceText(seg).length) {
addMatch(seg, pos + 1);
}
else if (seg < lastSourceIndex) {
addMatch(seg + 1, 0);
}
else {
return undefined;
}
}
matches.push(getLiteralType(str.slice(pos)));
addMatch(lastSourceIndex, getSourceText(lastSourceIndex).length);
return matches;
function getSourceText(index: number) {
return index < lastSourceIndex ? sourceTexts[index] : remainingEndText;
}
function addMatch(s: number, p: number) {
const matchType = s === seg ?
getLiteralType(getSourceText(s).slice(pos, p)) :
getTemplateLiteralType(
[sourceTexts[seg].slice(pos), ...sourceTexts.slice(seg + 1, s), getSourceText(s).slice(0, p)],
sourceTypes.slice(seg, s));
matches.push(matchType);
seg = s;
pos = p;
}
}
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) {
@@ -21189,9 +21249,7 @@ namespace ts {
}
function inferToTemplateLiteralType(source: Type, target: TemplateLiteralType) {
const matches = source.flags & TypeFlags.StringLiteral ? inferLiteralsFromTemplateLiteralType(<StringLiteralType>source, target) :
source.flags & TypeFlags.TemplateLiteral && arraysEqual((<TemplateLiteralType>source).texts, target.texts) ? (<TemplateLiteralType>source).types :
undefined;
const matches = inferTypesFromTemplateLiteralType(source, target);
const types = target.types;
for (let i = 0; i < types.length; i++) {
inferFromTypes(matches ? matches[i] : neverType, types[i]);

View File

@@ -0,0 +1,129 @@
tests/cases/conformance/types/literal/templateLiteralTypes3.ts(20,19): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
tests/cases/conformance/types/literal/templateLiteralTypes3.ts(57,5): error TS2322: Type '"hello"' is not assignable to type '`*${string}*`'.
tests/cases/conformance/types/literal/templateLiteralTypes3.ts(69,5): error TS2322: Type '"123"' is not assignable to type '`*${number}*`'.
tests/cases/conformance/types/literal/templateLiteralTypes3.ts(71,5): error TS2322: Type '"**123**"' is not assignable to type '`*${number}*`'.
tests/cases/conformance/types/literal/templateLiteralTypes3.ts(72,5): error TS2322: Type '`*${string}*`' is not assignable to type '`*${number}*`'.
tests/cases/conformance/types/literal/templateLiteralTypes3.ts(74,5): error TS2322: Type '"*false*" | "*true*"' is not assignable to type '`*${number}*`'.
Type '"*false*"' is not assignable to type '`*${number}*`'.
==== tests/cases/conformance/types/literal/templateLiteralTypes3.ts (6 errors) ====
// Inference from template literal type to template literal type
type Foo1<T> = T extends `*${infer U}*` ? U : never;
type T01 = Foo1<'hello'>;
type T02 = Foo1<'*hello*'>;
type T03 = Foo1<'**hello**'>;
type T04 = Foo1<`*${string}*`>;
type T05 = Foo1<`*${number}*`>;
type T06 = Foo1<`*${bigint}*`>;
type T07 = Foo1<`*${any}*`>;
type T08 = Foo1<`**${string}**`>;
type T09 = Foo1<`**${string}**${string}**`>;
type T10 = Foo1<`**${'a' | 'b' | 'c'}**`>;
type T11 = Foo1<`**${boolean}**${boolean}**`>;
declare function foo1<V extends string>(arg: `*${V}*`): V;
function f1<T extends string>(s: string, n: number, b: boolean, t: T) {
let x1 = foo1('hello'); // Error
~~~~~~~
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
let x2 = foo1('*hello*');
let x3 = foo1('**hello**');
let x4 = foo1(`*${s}*` as const);
let x5 = foo1(`*${n}*` as const);
let x6 = foo1(`*${b}*` as const);
let x7 = foo1(`*${t}*` as const);
let x8 = foo1(`**${s}**` as const);
}
// Inference to a placeholder immediately followed by another placeholder infers a single
// character or placeholder from the source.
type Parts<T> =
T extends '' ? [] :
T extends `${infer Head}${infer Tail}` ? [Head, ...Parts<Tail>] :
never;
type T20 = Parts<`abc`>;
type T21 = Parts<`*${string}*`>;
type T22 = Parts<`*${number}*`>;
type T23 = Parts<`*${number}*${string}*${bigint}*`>;
function f2() {
let x: `${number}.${number}.${number}`;
x = '1.1.1';
x = '1.1.1' as `1.1.${number}`;
x = '1.1.1' as `1.${number}.1`;
x = '1.1.1' as `1.${number}.${number}`;
x = '1.1.1' as `${number}.1.1`;
x = '1.1.1' as `${number}.1.${number}`;
x = '1.1.1' as `${number}.${number}.1`;
x = '1.1.1' as `${number}.${number}.${number}`;
}
function f3<T extends string>(s: string, n: number, b: boolean, t: T) {
let x: `*${string}*`;
x = 'hello'; // Error
~
!!! error TS2322: Type '"hello"' is not assignable to type '`*${string}*`'.
x = '*hello*';
x = '**hello**';
x = `*${s}*` as const;
x = `*${n}*` as const;
x = `*${b}*` as const;
x = `*${t}*` as const;
x = `**${s}**` as const;
}
function f4<T extends number>(s: string, n: number, b: boolean, t: T) {
let x: `*${number}*`;
x = '123'; // Error
~
!!! error TS2322: Type '"123"' is not assignable to type '`*${number}*`'.
x = '*123*';
x = '**123**'; // Error
~
!!! error TS2322: Type '"**123**"' is not assignable to type '`*${number}*`'.
x = `*${s}*` as const; // Error
~
!!! error TS2322: Type '`*${string}*`' is not assignable to type '`*${number}*`'.
x = `*${n}*` as const;
x = `*${b}*` as const; // Error
~
!!! error TS2322: Type '"*false*" | "*true*"' is not assignable to type '`*${number}*`'.
!!! error TS2322: Type '"*false*"' is not assignable to type '`*${number}*`'.
x = `*${t}*` as const;
}
// Repro from #43060
type A<T> = T extends `${infer U}.${infer V}` ? U | V : never
type B = A<`test.1024`>; // "test" | "1024"
type C = A<`test.${number}`>; // "test" | `${number}`
type D<T> = T extends `${infer U}.${number}` ? U : never
type E = D<`test.1024`>; // "test"
type F = D<`test.${number}`>; // "test"
type G<T> = T extends `${infer U}.${infer V}` ? U | V : never
type H = G<`test.hoge`>; // "test" | "hoge"
type I = G<`test.${string}`>; // string ("test" | string reduces to string)
type J<T> = T extends `${infer U}.${string}` ? U : never
type K = J<`test.hoge`>; // "test"
type L = J<`test.${string}`>; // "test""
// Repro from #43243
type Templated = `${string} ${string}`;
const value1: string = "abc";
const templated1: Templated = `${value1} abc` as const;
// Type '`${string} abc`' is not assignable to type '`${string} ${string}`'.
const value2 = "abc";
const templated2: Templated = `${value2} abc` as const;

View File

@@ -0,0 +1,200 @@
//// [templateLiteralTypes3.ts]
// Inference from template literal type to template literal type
type Foo1<T> = T extends `*${infer U}*` ? U : never;
type T01 = Foo1<'hello'>;
type T02 = Foo1<'*hello*'>;
type T03 = Foo1<'**hello**'>;
type T04 = Foo1<`*${string}*`>;
type T05 = Foo1<`*${number}*`>;
type T06 = Foo1<`*${bigint}*`>;
type T07 = Foo1<`*${any}*`>;
type T08 = Foo1<`**${string}**`>;
type T09 = Foo1<`**${string}**${string}**`>;
type T10 = Foo1<`**${'a' | 'b' | 'c'}**`>;
type T11 = Foo1<`**${boolean}**${boolean}**`>;
declare function foo1<V extends string>(arg: `*${V}*`): V;
function f1<T extends string>(s: string, n: number, b: boolean, t: T) {
let x1 = foo1('hello'); // Error
let x2 = foo1('*hello*');
let x3 = foo1('**hello**');
let x4 = foo1(`*${s}*` as const);
let x5 = foo1(`*${n}*` as const);
let x6 = foo1(`*${b}*` as const);
let x7 = foo1(`*${t}*` as const);
let x8 = foo1(`**${s}**` as const);
}
// Inference to a placeholder immediately followed by another placeholder infers a single
// character or placeholder from the source.
type Parts<T> =
T extends '' ? [] :
T extends `${infer Head}${infer Tail}` ? [Head, ...Parts<Tail>] :
never;
type T20 = Parts<`abc`>;
type T21 = Parts<`*${string}*`>;
type T22 = Parts<`*${number}*`>;
type T23 = Parts<`*${number}*${string}*${bigint}*`>;
function f2() {
let x: `${number}.${number}.${number}`;
x = '1.1.1';
x = '1.1.1' as `1.1.${number}`;
x = '1.1.1' as `1.${number}.1`;
x = '1.1.1' as `1.${number}.${number}`;
x = '1.1.1' as `${number}.1.1`;
x = '1.1.1' as `${number}.1.${number}`;
x = '1.1.1' as `${number}.${number}.1`;
x = '1.1.1' as `${number}.${number}.${number}`;
}
function f3<T extends string>(s: string, n: number, b: boolean, t: T) {
let x: `*${string}*`;
x = 'hello'; // Error
x = '*hello*';
x = '**hello**';
x = `*${s}*` as const;
x = `*${n}*` as const;
x = `*${b}*` as const;
x = `*${t}*` as const;
x = `**${s}**` as const;
}
function f4<T extends number>(s: string, n: number, b: boolean, t: T) {
let x: `*${number}*`;
x = '123'; // Error
x = '*123*';
x = '**123**'; // Error
x = `*${s}*` as const; // Error
x = `*${n}*` as const;
x = `*${b}*` as const; // Error
x = `*${t}*` as const;
}
// Repro from #43060
type A<T> = T extends `${infer U}.${infer V}` ? U | V : never
type B = A<`test.1024`>; // "test" | "1024"
type C = A<`test.${number}`>; // "test" | `${number}`
type D<T> = T extends `${infer U}.${number}` ? U : never
type E = D<`test.1024`>; // "test"
type F = D<`test.${number}`>; // "test"
type G<T> = T extends `${infer U}.${infer V}` ? U | V : never
type H = G<`test.hoge`>; // "test" | "hoge"
type I = G<`test.${string}`>; // string ("test" | string reduces to string)
type J<T> = T extends `${infer U}.${string}` ? U : never
type K = J<`test.hoge`>; // "test"
type L = J<`test.${string}`>; // "test""
// Repro from #43243
type Templated = `${string} ${string}`;
const value1: string = "abc";
const templated1: Templated = `${value1} abc` as const;
// Type '`${string} abc`' is not assignable to type '`${string} ${string}`'.
const value2 = "abc";
const templated2: Templated = `${value2} abc` as const;
//// [templateLiteralTypes3.js]
"use strict";
// Inference from template literal type to template literal type
function f1(s, n, b, t) {
var x1 = foo1('hello'); // Error
var x2 = foo1('*hello*');
var x3 = foo1('**hello**');
var x4 = foo1("*" + s + "*");
var x5 = foo1("*" + n + "*");
var x6 = foo1("*" + b + "*");
var x7 = foo1("*" + t + "*");
var x8 = foo1("**" + s + "**");
}
function f2() {
var x;
x = '1.1.1';
x = '1.1.1';
x = '1.1.1';
x = '1.1.1';
x = '1.1.1';
x = '1.1.1';
x = '1.1.1';
x = '1.1.1';
}
function f3(s, n, b, t) {
var x;
x = 'hello'; // Error
x = '*hello*';
x = '**hello**';
x = "*" + s + "*";
x = "*" + n + "*";
x = "*" + b + "*";
x = "*" + t + "*";
x = "**" + s + "**";
}
function f4(s, n, b, t) {
var x;
x = '123'; // Error
x = '*123*';
x = '**123**'; // Error
x = "*" + s + "*"; // Error
x = "*" + n + "*";
x = "*" + b + "*"; // Error
x = "*" + t + "*";
}
var value1 = "abc";
var templated1 = value1 + " abc";
// Type '`${string} abc`' is not assignable to type '`${string} ${string}`'.
var value2 = "abc";
var templated2 = value2 + " abc";
//// [templateLiteralTypes3.d.ts]
declare type Foo1<T> = T extends `*${infer U}*` ? U : never;
declare type T01 = Foo1<'hello'>;
declare type T02 = Foo1<'*hello*'>;
declare type T03 = Foo1<'**hello**'>;
declare type T04 = Foo1<`*${string}*`>;
declare type T05 = Foo1<`*${number}*`>;
declare type T06 = Foo1<`*${bigint}*`>;
declare type T07 = Foo1<`*${any}*`>;
declare type T08 = Foo1<`**${string}**`>;
declare type T09 = Foo1<`**${string}**${string}**`>;
declare type T10 = Foo1<`**${'a' | 'b' | 'c'}**`>;
declare type T11 = Foo1<`**${boolean}**${boolean}**`>;
declare function foo1<V extends string>(arg: `*${V}*`): V;
declare function f1<T extends string>(s: string, n: number, b: boolean, t: T): void;
declare type Parts<T> = T extends '' ? [] : T extends `${infer Head}${infer Tail}` ? [Head, ...Parts<Tail>] : never;
declare type T20 = Parts<`abc`>;
declare type T21 = Parts<`*${string}*`>;
declare type T22 = Parts<`*${number}*`>;
declare type T23 = Parts<`*${number}*${string}*${bigint}*`>;
declare function f2(): void;
declare function f3<T extends string>(s: string, n: number, b: boolean, t: T): void;
declare function f4<T extends number>(s: string, n: number, b: boolean, t: T): void;
declare type A<T> = T extends `${infer U}.${infer V}` ? U | V : never;
declare type B = A<`test.1024`>;
declare type C = A<`test.${number}`>;
declare type D<T> = T extends `${infer U}.${number}` ? U : never;
declare type E = D<`test.1024`>;
declare type F = D<`test.${number}`>;
declare type G<T> = T extends `${infer U}.${infer V}` ? U | V : never;
declare type H = G<`test.hoge`>;
declare type I = G<`test.${string}`>;
declare type J<T> = T extends `${infer U}.${string}` ? U : never;
declare type K = J<`test.hoge`>;
declare type L = J<`test.${string}`>;
declare type Templated = `${string} ${string}`;
declare const value1: string;
declare const templated1: Templated;
declare const value2 = "abc";
declare const templated2: Templated;

View File

@@ -0,0 +1,344 @@
=== tests/cases/conformance/types/literal/templateLiteralTypes3.ts ===
// Inference from template literal type to template literal type
type Foo1<T> = T extends `*${infer U}*` ? U : never;
>Foo1 : Symbol(Foo1, Decl(templateLiteralTypes3.ts, 0, 0))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 2, 10))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 2, 10))
>U : Symbol(U, Decl(templateLiteralTypes3.ts, 2, 34))
>U : Symbol(U, Decl(templateLiteralTypes3.ts, 2, 34))
type T01 = Foo1<'hello'>;
>T01 : Symbol(T01, Decl(templateLiteralTypes3.ts, 2, 52))
>Foo1 : Symbol(Foo1, Decl(templateLiteralTypes3.ts, 0, 0))
type T02 = Foo1<'*hello*'>;
>T02 : Symbol(T02, Decl(templateLiteralTypes3.ts, 4, 25))
>Foo1 : Symbol(Foo1, Decl(templateLiteralTypes3.ts, 0, 0))
type T03 = Foo1<'**hello**'>;
>T03 : Symbol(T03, Decl(templateLiteralTypes3.ts, 5, 27))
>Foo1 : Symbol(Foo1, Decl(templateLiteralTypes3.ts, 0, 0))
type T04 = Foo1<`*${string}*`>;
>T04 : Symbol(T04, Decl(templateLiteralTypes3.ts, 6, 29))
>Foo1 : Symbol(Foo1, Decl(templateLiteralTypes3.ts, 0, 0))
type T05 = Foo1<`*${number}*`>;
>T05 : Symbol(T05, Decl(templateLiteralTypes3.ts, 7, 31))
>Foo1 : Symbol(Foo1, Decl(templateLiteralTypes3.ts, 0, 0))
type T06 = Foo1<`*${bigint}*`>;
>T06 : Symbol(T06, Decl(templateLiteralTypes3.ts, 8, 31))
>Foo1 : Symbol(Foo1, Decl(templateLiteralTypes3.ts, 0, 0))
type T07 = Foo1<`*${any}*`>;
>T07 : Symbol(T07, Decl(templateLiteralTypes3.ts, 9, 31))
>Foo1 : Symbol(Foo1, Decl(templateLiteralTypes3.ts, 0, 0))
type T08 = Foo1<`**${string}**`>;
>T08 : Symbol(T08, Decl(templateLiteralTypes3.ts, 10, 28))
>Foo1 : Symbol(Foo1, Decl(templateLiteralTypes3.ts, 0, 0))
type T09 = Foo1<`**${string}**${string}**`>;
>T09 : Symbol(T09, Decl(templateLiteralTypes3.ts, 11, 33))
>Foo1 : Symbol(Foo1, Decl(templateLiteralTypes3.ts, 0, 0))
type T10 = Foo1<`**${'a' | 'b' | 'c'}**`>;
>T10 : Symbol(T10, Decl(templateLiteralTypes3.ts, 12, 44))
>Foo1 : Symbol(Foo1, Decl(templateLiteralTypes3.ts, 0, 0))
type T11 = Foo1<`**${boolean}**${boolean}**`>;
>T11 : Symbol(T11, Decl(templateLiteralTypes3.ts, 13, 42))
>Foo1 : Symbol(Foo1, Decl(templateLiteralTypes3.ts, 0, 0))
declare function foo1<V extends string>(arg: `*${V}*`): V;
>foo1 : Symbol(foo1, Decl(templateLiteralTypes3.ts, 14, 46))
>V : Symbol(V, Decl(templateLiteralTypes3.ts, 16, 22))
>arg : Symbol(arg, Decl(templateLiteralTypes3.ts, 16, 40))
>V : Symbol(V, Decl(templateLiteralTypes3.ts, 16, 22))
>V : Symbol(V, Decl(templateLiteralTypes3.ts, 16, 22))
function f1<T extends string>(s: string, n: number, b: boolean, t: T) {
>f1 : Symbol(f1, Decl(templateLiteralTypes3.ts, 16, 58))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 18, 12))
>s : Symbol(s, Decl(templateLiteralTypes3.ts, 18, 30))
>n : Symbol(n, Decl(templateLiteralTypes3.ts, 18, 40))
>b : Symbol(b, Decl(templateLiteralTypes3.ts, 18, 51))
>t : Symbol(t, Decl(templateLiteralTypes3.ts, 18, 63))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 18, 12))
let x1 = foo1('hello'); // Error
>x1 : Symbol(x1, Decl(templateLiteralTypes3.ts, 19, 7))
>foo1 : Symbol(foo1, Decl(templateLiteralTypes3.ts, 14, 46))
let x2 = foo1('*hello*');
>x2 : Symbol(x2, Decl(templateLiteralTypes3.ts, 20, 7))
>foo1 : Symbol(foo1, Decl(templateLiteralTypes3.ts, 14, 46))
let x3 = foo1('**hello**');
>x3 : Symbol(x3, Decl(templateLiteralTypes3.ts, 21, 7))
>foo1 : Symbol(foo1, Decl(templateLiteralTypes3.ts, 14, 46))
let x4 = foo1(`*${s}*` as const);
>x4 : Symbol(x4, Decl(templateLiteralTypes3.ts, 22, 7))
>foo1 : Symbol(foo1, Decl(templateLiteralTypes3.ts, 14, 46))
>s : Symbol(s, Decl(templateLiteralTypes3.ts, 18, 30))
let x5 = foo1(`*${n}*` as const);
>x5 : Symbol(x5, Decl(templateLiteralTypes3.ts, 23, 7))
>foo1 : Symbol(foo1, Decl(templateLiteralTypes3.ts, 14, 46))
>n : Symbol(n, Decl(templateLiteralTypes3.ts, 18, 40))
let x6 = foo1(`*${b}*` as const);
>x6 : Symbol(x6, Decl(templateLiteralTypes3.ts, 24, 7))
>foo1 : Symbol(foo1, Decl(templateLiteralTypes3.ts, 14, 46))
>b : Symbol(b, Decl(templateLiteralTypes3.ts, 18, 51))
let x7 = foo1(`*${t}*` as const);
>x7 : Symbol(x7, Decl(templateLiteralTypes3.ts, 25, 7))
>foo1 : Symbol(foo1, Decl(templateLiteralTypes3.ts, 14, 46))
>t : Symbol(t, Decl(templateLiteralTypes3.ts, 18, 63))
let x8 = foo1(`**${s}**` as const);
>x8 : Symbol(x8, Decl(templateLiteralTypes3.ts, 26, 7))
>foo1 : Symbol(foo1, Decl(templateLiteralTypes3.ts, 14, 46))
>s : Symbol(s, Decl(templateLiteralTypes3.ts, 18, 30))
}
// Inference to a placeholder immediately followed by another placeholder infers a single
// character or placeholder from the source.
type Parts<T> =
>Parts : Symbol(Parts, Decl(templateLiteralTypes3.ts, 27, 1))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 32, 11))
T extends '' ? [] :
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 32, 11))
T extends `${infer Head}${infer Tail}` ? [Head, ...Parts<Tail>] :
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 32, 11))
>Head : Symbol(Head, Decl(templateLiteralTypes3.ts, 34, 22))
>Tail : Symbol(Tail, Decl(templateLiteralTypes3.ts, 34, 35))
>Head : Symbol(Head, Decl(templateLiteralTypes3.ts, 34, 22))
>Parts : Symbol(Parts, Decl(templateLiteralTypes3.ts, 27, 1))
>Tail : Symbol(Tail, Decl(templateLiteralTypes3.ts, 34, 35))
never;
type T20 = Parts<`abc`>;
>T20 : Symbol(T20, Decl(templateLiteralTypes3.ts, 35, 10))
>Parts : Symbol(Parts, Decl(templateLiteralTypes3.ts, 27, 1))
type T21 = Parts<`*${string}*`>;
>T21 : Symbol(T21, Decl(templateLiteralTypes3.ts, 37, 24))
>Parts : Symbol(Parts, Decl(templateLiteralTypes3.ts, 27, 1))
type T22 = Parts<`*${number}*`>;
>T22 : Symbol(T22, Decl(templateLiteralTypes3.ts, 38, 32))
>Parts : Symbol(Parts, Decl(templateLiteralTypes3.ts, 27, 1))
type T23 = Parts<`*${number}*${string}*${bigint}*`>;
>T23 : Symbol(T23, Decl(templateLiteralTypes3.ts, 39, 32))
>Parts : Symbol(Parts, Decl(templateLiteralTypes3.ts, 27, 1))
function f2() {
>f2 : Symbol(f2, Decl(templateLiteralTypes3.ts, 40, 52))
let x: `${number}.${number}.${number}`;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 43, 7))
x = '1.1.1';
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 43, 7))
x = '1.1.1' as `1.1.${number}`;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 43, 7))
x = '1.1.1' as `1.${number}.1`;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 43, 7))
x = '1.1.1' as `1.${number}.${number}`;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 43, 7))
x = '1.1.1' as `${number}.1.1`;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 43, 7))
x = '1.1.1' as `${number}.1.${number}`;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 43, 7))
x = '1.1.1' as `${number}.${number}.1`;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 43, 7))
x = '1.1.1' as `${number}.${number}.${number}`;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 43, 7))
}
function f3<T extends string>(s: string, n: number, b: boolean, t: T) {
>f3 : Symbol(f3, Decl(templateLiteralTypes3.ts, 52, 1))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 54, 12))
>s : Symbol(s, Decl(templateLiteralTypes3.ts, 54, 30))
>n : Symbol(n, Decl(templateLiteralTypes3.ts, 54, 40))
>b : Symbol(b, Decl(templateLiteralTypes3.ts, 54, 51))
>t : Symbol(t, Decl(templateLiteralTypes3.ts, 54, 63))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 54, 12))
let x: `*${string}*`;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 55, 7))
x = 'hello'; // Error
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 55, 7))
x = '*hello*';
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 55, 7))
x = '**hello**';
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 55, 7))
x = `*${s}*` as const;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 55, 7))
>s : Symbol(s, Decl(templateLiteralTypes3.ts, 54, 30))
x = `*${n}*` as const;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 55, 7))
>n : Symbol(n, Decl(templateLiteralTypes3.ts, 54, 40))
x = `*${b}*` as const;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 55, 7))
>b : Symbol(b, Decl(templateLiteralTypes3.ts, 54, 51))
x = `*${t}*` as const;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 55, 7))
>t : Symbol(t, Decl(templateLiteralTypes3.ts, 54, 63))
x = `**${s}**` as const;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 55, 7))
>s : Symbol(s, Decl(templateLiteralTypes3.ts, 54, 30))
}
function f4<T extends number>(s: string, n: number, b: boolean, t: T) {
>f4 : Symbol(f4, Decl(templateLiteralTypes3.ts, 64, 1))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 66, 12))
>s : Symbol(s, Decl(templateLiteralTypes3.ts, 66, 30))
>n : Symbol(n, Decl(templateLiteralTypes3.ts, 66, 40))
>b : Symbol(b, Decl(templateLiteralTypes3.ts, 66, 51))
>t : Symbol(t, Decl(templateLiteralTypes3.ts, 66, 63))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 66, 12))
let x: `*${number}*`;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 67, 7))
x = '123'; // Error
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 67, 7))
x = '*123*';
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 67, 7))
x = '**123**'; // Error
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 67, 7))
x = `*${s}*` as const; // Error
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 67, 7))
>s : Symbol(s, Decl(templateLiteralTypes3.ts, 66, 30))
x = `*${n}*` as const;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 67, 7))
>n : Symbol(n, Decl(templateLiteralTypes3.ts, 66, 40))
x = `*${b}*` as const; // Error
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 67, 7))
>b : Symbol(b, Decl(templateLiteralTypes3.ts, 66, 51))
x = `*${t}*` as const;
>x : Symbol(x, Decl(templateLiteralTypes3.ts, 67, 7))
>t : Symbol(t, Decl(templateLiteralTypes3.ts, 66, 63))
}
// Repro from #43060
type A<T> = T extends `${infer U}.${infer V}` ? U | V : never
>A : Symbol(A, Decl(templateLiteralTypes3.ts, 75, 1))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 79, 7))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 79, 7))
>U : Symbol(U, Decl(templateLiteralTypes3.ts, 79, 30))
>V : Symbol(V, Decl(templateLiteralTypes3.ts, 79, 41))
>U : Symbol(U, Decl(templateLiteralTypes3.ts, 79, 30))
>V : Symbol(V, Decl(templateLiteralTypes3.ts, 79, 41))
type B = A<`test.1024`>; // "test" | "1024"
>B : Symbol(B, Decl(templateLiteralTypes3.ts, 79, 61))
>A : Symbol(A, Decl(templateLiteralTypes3.ts, 75, 1))
type C = A<`test.${number}`>; // "test" | `${number}`
>C : Symbol(C, Decl(templateLiteralTypes3.ts, 80, 24))
>A : Symbol(A, Decl(templateLiteralTypes3.ts, 75, 1))
type D<T> = T extends `${infer U}.${number}` ? U : never
>D : Symbol(D, Decl(templateLiteralTypes3.ts, 81, 29))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 83, 7))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 83, 7))
>U : Symbol(U, Decl(templateLiteralTypes3.ts, 83, 30))
>U : Symbol(U, Decl(templateLiteralTypes3.ts, 83, 30))
type E = D<`test.1024`>; // "test"
>E : Symbol(E, Decl(templateLiteralTypes3.ts, 83, 56))
>D : Symbol(D, Decl(templateLiteralTypes3.ts, 81, 29))
type F = D<`test.${number}`>; // "test"
>F : Symbol(F, Decl(templateLiteralTypes3.ts, 84, 24))
>D : Symbol(D, Decl(templateLiteralTypes3.ts, 81, 29))
type G<T> = T extends `${infer U}.${infer V}` ? U | V : never
>G : Symbol(G, Decl(templateLiteralTypes3.ts, 85, 29))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 87, 7))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 87, 7))
>U : Symbol(U, Decl(templateLiteralTypes3.ts, 87, 30))
>V : Symbol(V, Decl(templateLiteralTypes3.ts, 87, 41))
>U : Symbol(U, Decl(templateLiteralTypes3.ts, 87, 30))
>V : Symbol(V, Decl(templateLiteralTypes3.ts, 87, 41))
type H = G<`test.hoge`>; // "test" | "hoge"
>H : Symbol(H, Decl(templateLiteralTypes3.ts, 87, 61))
>G : Symbol(G, Decl(templateLiteralTypes3.ts, 85, 29))
type I = G<`test.${string}`>; // string ("test" | string reduces to string)
>I : Symbol(I, Decl(templateLiteralTypes3.ts, 88, 24))
>G : Symbol(G, Decl(templateLiteralTypes3.ts, 85, 29))
type J<T> = T extends `${infer U}.${string}` ? U : never
>J : Symbol(J, Decl(templateLiteralTypes3.ts, 89, 29))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 91, 7))
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 91, 7))
>U : Symbol(U, Decl(templateLiteralTypes3.ts, 91, 30))
>U : Symbol(U, Decl(templateLiteralTypes3.ts, 91, 30))
type K = J<`test.hoge`>; // "test"
>K : Symbol(K, Decl(templateLiteralTypes3.ts, 91, 56))
>J : Symbol(J, Decl(templateLiteralTypes3.ts, 89, 29))
type L = J<`test.${string}`>; // "test""
>L : Symbol(L, Decl(templateLiteralTypes3.ts, 92, 24))
>J : Symbol(J, Decl(templateLiteralTypes3.ts, 89, 29))
// Repro from #43243
type Templated = `${string} ${string}`;
>Templated : Symbol(Templated, Decl(templateLiteralTypes3.ts, 93, 29))
const value1: string = "abc";
>value1 : Symbol(value1, Decl(templateLiteralTypes3.ts, 99, 5))
const templated1: Templated = `${value1} abc` as const;
>templated1 : Symbol(templated1, Decl(templateLiteralTypes3.ts, 100, 5))
>Templated : Symbol(Templated, Decl(templateLiteralTypes3.ts, 93, 29))
>value1 : Symbol(value1, Decl(templateLiteralTypes3.ts, 99, 5))
// Type '`${string} abc`' is not assignable to type '`${string} ${string}`'.
const value2 = "abc";
>value2 : Symbol(value2, Decl(templateLiteralTypes3.ts, 103, 5))
const templated2: Templated = `${value2} abc` as const;
>templated2 : Symbol(templated2, Decl(templateLiteralTypes3.ts, 104, 5))
>Templated : Symbol(Templated, Decl(templateLiteralTypes3.ts, 93, 29))
>value2 : Symbol(value2, Decl(templateLiteralTypes3.ts, 103, 5))

View File

@@ -0,0 +1,365 @@
=== tests/cases/conformance/types/literal/templateLiteralTypes3.ts ===
// Inference from template literal type to template literal type
type Foo1<T> = T extends `*${infer U}*` ? U : never;
>Foo1 : Foo1<T>
type T01 = Foo1<'hello'>;
>T01 : never
type T02 = Foo1<'*hello*'>;
>T02 : "hello"
type T03 = Foo1<'**hello**'>;
>T03 : "*hello*"
type T04 = Foo1<`*${string}*`>;
>T04 : string
type T05 = Foo1<`*${number}*`>;
>T05 : `${number}`
type T06 = Foo1<`*${bigint}*`>;
>T06 : `${bigint}`
type T07 = Foo1<`*${any}*`>;
>T07 : any
type T08 = Foo1<`**${string}**`>;
>T08 : `*${string}*`
type T09 = Foo1<`**${string}**${string}**`>;
>T09 : `*${string}**${string}*`
type T10 = Foo1<`**${'a' | 'b' | 'c'}**`>;
>T10 : T10
type T11 = Foo1<`**${boolean}**${boolean}**`>;
>T11 : T11
declare function foo1<V extends string>(arg: `*${V}*`): V;
>foo1 : <V extends string>(arg: `*${V}*`) => V
>arg : `*${V}*`
function f1<T extends string>(s: string, n: number, b: boolean, t: T) {
>f1 : <T extends string>(s: string, n: number, b: boolean, t: T) => void
>s : string
>n : number
>b : boolean
>t : T
let x1 = foo1('hello'); // Error
>x1 : never
>foo1('hello') : never
>foo1 : <V extends string>(arg: `*${V}*`) => V
>'hello' : "hello"
let x2 = foo1('*hello*');
>x2 : "hello"
>foo1('*hello*') : "hello"
>foo1 : <V extends string>(arg: `*${V}*`) => V
>'*hello*' : "*hello*"
let x3 = foo1('**hello**');
>x3 : "*hello*"
>foo1('**hello**') : "*hello*"
>foo1 : <V extends string>(arg: `*${V}*`) => V
>'**hello**' : "**hello**"
let x4 = foo1(`*${s}*` as const);
>x4 : string
>foo1(`*${s}*` as const) : string
>foo1 : <V extends string>(arg: `*${V}*`) => V
>`*${s}*` as const : `*${string}*`
>`*${s}*` : `*${string}*`
>s : string
let x5 = foo1(`*${n}*` as const);
>x5 : `${number}`
>foo1(`*${n}*` as const) : `${number}`
>foo1 : <V extends string>(arg: `*${V}*`) => V
>`*${n}*` as const : `*${number}*`
>`*${n}*` : `*${number}*`
>n : number
let x6 = foo1(`*${b}*` as const);
>x6 : "false" | "true"
>foo1(`*${b}*` as const) : "false" | "true"
>foo1 : <V extends string>(arg: `*${V}*`) => V
>`*${b}*` as const : "*false*" | "*true*"
>`*${b}*` : "*false*" | "*true*"
>b : boolean
let x7 = foo1(`*${t}*` as const);
>x7 : `${T}`
>foo1(`*${t}*` as const) : `${T}`
>foo1 : <V extends string>(arg: `*${V}*`) => V
>`*${t}*` as const : `*${T}*`
>`*${t}*` : `*${T}*`
>t : T
let x8 = foo1(`**${s}**` as const);
>x8 : `*${string}*`
>foo1(`**${s}**` as const) : `*${string}*`
>foo1 : <V extends string>(arg: `*${V}*`) => V
>`**${s}**` as const : `**${string}**`
>`**${s}**` : `**${string}**`
>s : string
}
// Inference to a placeholder immediately followed by another placeholder infers a single
// character or placeholder from the source.
type Parts<T> =
>Parts : Parts<T>
T extends '' ? [] :
T extends `${infer Head}${infer Tail}` ? [Head, ...Parts<Tail>] :
never;
type T20 = Parts<`abc`>;
>T20 : ["a", "b", "c"]
type T21 = Parts<`*${string}*`>;
>T21 : ["*", string, "*"]
type T22 = Parts<`*${number}*`>;
>T22 : ["*", `${number}`, "*"]
type T23 = Parts<`*${number}*${string}*${bigint}*`>;
>T23 : ["*", `${number}`, "*", string, "*", `${bigint}`, "*"]
function f2() {
>f2 : () => void
let x: `${number}.${number}.${number}`;
>x : `${number}.${number}.${number}`
x = '1.1.1';
>x = '1.1.1' : "1.1.1"
>x : `${number}.${number}.${number}`
>'1.1.1' : "1.1.1"
x = '1.1.1' as `1.1.${number}`;
>x = '1.1.1' as `1.1.${number}` : `1.1.${number}`
>x : `${number}.${number}.${number}`
>'1.1.1' as `1.1.${number}` : `1.1.${number}`
>'1.1.1' : "1.1.1"
x = '1.1.1' as `1.${number}.1`;
>x = '1.1.1' as `1.${number}.1` : `1.${number}.1`
>x : `${number}.${number}.${number}`
>'1.1.1' as `1.${number}.1` : `1.${number}.1`
>'1.1.1' : "1.1.1"
x = '1.1.1' as `1.${number}.${number}`;
>x = '1.1.1' as `1.${number}.${number}` : `1.${number}.${number}`
>x : `${number}.${number}.${number}`
>'1.1.1' as `1.${number}.${number}` : `1.${number}.${number}`
>'1.1.1' : "1.1.1"
x = '1.1.1' as `${number}.1.1`;
>x = '1.1.1' as `${number}.1.1` : `${number}.1.1`
>x : `${number}.${number}.${number}`
>'1.1.1' as `${number}.1.1` : `${number}.1.1`
>'1.1.1' : "1.1.1"
x = '1.1.1' as `${number}.1.${number}`;
>x = '1.1.1' as `${number}.1.${number}` : `${number}.1.${number}`
>x : `${number}.${number}.${number}`
>'1.1.1' as `${number}.1.${number}` : `${number}.1.${number}`
>'1.1.1' : "1.1.1"
x = '1.1.1' as `${number}.${number}.1`;
>x = '1.1.1' as `${number}.${number}.1` : `${number}.${number}.1`
>x : `${number}.${number}.${number}`
>'1.1.1' as `${number}.${number}.1` : `${number}.${number}.1`
>'1.1.1' : "1.1.1"
x = '1.1.1' as `${number}.${number}.${number}`;
>x = '1.1.1' as `${number}.${number}.${number}` : `${number}.${number}.${number}`
>x : `${number}.${number}.${number}`
>'1.1.1' as `${number}.${number}.${number}` : `${number}.${number}.${number}`
>'1.1.1' : "1.1.1"
}
function f3<T extends string>(s: string, n: number, b: boolean, t: T) {
>f3 : <T extends string>(s: string, n: number, b: boolean, t: T) => void
>s : string
>n : number
>b : boolean
>t : T
let x: `*${string}*`;
>x : `*${string}*`
x = 'hello'; // Error
>x = 'hello' : "hello"
>x : `*${string}*`
>'hello' : "hello"
x = '*hello*';
>x = '*hello*' : "*hello*"
>x : `*${string}*`
>'*hello*' : "*hello*"
x = '**hello**';
>x = '**hello**' : "**hello**"
>x : `*${string}*`
>'**hello**' : "**hello**"
x = `*${s}*` as const;
>x = `*${s}*` as const : `*${string}*`
>x : `*${string}*`
>`*${s}*` as const : `*${string}*`
>`*${s}*` : `*${string}*`
>s : string
x = `*${n}*` as const;
>x = `*${n}*` as const : `*${number}*`
>x : `*${string}*`
>`*${n}*` as const : `*${number}*`
>`*${n}*` : `*${number}*`
>n : number
x = `*${b}*` as const;
>x = `*${b}*` as const : "*false*" | "*true*"
>x : `*${string}*`
>`*${b}*` as const : "*false*" | "*true*"
>`*${b}*` : "*false*" | "*true*"
>b : boolean
x = `*${t}*` as const;
>x = `*${t}*` as const : `*${T}*`
>x : `*${string}*`
>`*${t}*` as const : `*${T}*`
>`*${t}*` : `*${T}*`
>t : T
x = `**${s}**` as const;
>x = `**${s}**` as const : `**${string}**`
>x : `*${string}*`
>`**${s}**` as const : `**${string}**`
>`**${s}**` : `**${string}**`
>s : string
}
function f4<T extends number>(s: string, n: number, b: boolean, t: T) {
>f4 : <T extends number>(s: string, n: number, b: boolean, t: T) => void
>s : string
>n : number
>b : boolean
>t : T
let x: `*${number}*`;
>x : `*${number}*`
x = '123'; // Error
>x = '123' : "123"
>x : `*${number}*`
>'123' : "123"
x = '*123*';
>x = '*123*' : "*123*"
>x : `*${number}*`
>'*123*' : "*123*"
x = '**123**'; // Error
>x = '**123**' : "**123**"
>x : `*${number}*`
>'**123**' : "**123**"
x = `*${s}*` as const; // Error
>x = `*${s}*` as const : `*${string}*`
>x : `*${number}*`
>`*${s}*` as const : `*${string}*`
>`*${s}*` : `*${string}*`
>s : string
x = `*${n}*` as const;
>x = `*${n}*` as const : `*${number}*`
>x : `*${number}*`
>`*${n}*` as const : `*${number}*`
>`*${n}*` : `*${number}*`
>n : number
x = `*${b}*` as const; // Error
>x = `*${b}*` as const : "*false*" | "*true*"
>x : `*${number}*`
>`*${b}*` as const : "*false*" | "*true*"
>`*${b}*` : "*false*" | "*true*"
>b : boolean
x = `*${t}*` as const;
>x = `*${t}*` as const : `*${T}*`
>x : `*${number}*`
>`*${t}*` as const : `*${T}*`
>`*${t}*` : `*${T}*`
>t : T
}
// Repro from #43060
type A<T> = T extends `${infer U}.${infer V}` ? U | V : never
>A : A<T>
type B = A<`test.1024`>; // "test" | "1024"
>B : "test" | "1024"
type C = A<`test.${number}`>; // "test" | `${number}`
>C : `${number}` | "test"
type D<T> = T extends `${infer U}.${number}` ? U : never
>D : D<T>
type E = D<`test.1024`>; // "test"
>E : "test"
type F = D<`test.${number}`>; // "test"
>F : "test"
type G<T> = T extends `${infer U}.${infer V}` ? U | V : never
>G : G<T>
type H = G<`test.hoge`>; // "test" | "hoge"
>H : "test" | "hoge"
type I = G<`test.${string}`>; // string ("test" | string reduces to string)
>I : string
type J<T> = T extends `${infer U}.${string}` ? U : never
>J : J<T>
type K = J<`test.hoge`>; // "test"
>K : "test"
type L = J<`test.${string}`>; // "test""
>L : "test"
// Repro from #43243
type Templated = `${string} ${string}`;
>Templated : `${string} ${string}`
const value1: string = "abc";
>value1 : string
>"abc" : "abc"
const templated1: Templated = `${value1} abc` as const;
>templated1 : `${string} ${string}`
>`${value1} abc` as const : `${string} abc`
>`${value1} abc` : `${string} abc`
>value1 : string
// Type '`${string} abc`' is not assignable to type '`${string} ${string}`'.
const value2 = "abc";
>value2 : "abc"
>"abc" : "abc"
const templated2: Templated = `${value2} abc` as const;
>templated2 : `${string} ${string}`
>`${value2} abc` as const : "abc abc"
>`${value2} abc` : "abc abc"
>value2 : "abc"

View File

@@ -0,0 +1,108 @@
// @strict: true
// @declaration: true
// Inference from template literal type to template literal type
type Foo1<T> = T extends `*${infer U}*` ? U : never;
type T01 = Foo1<'hello'>;
type T02 = Foo1<'*hello*'>;
type T03 = Foo1<'**hello**'>;
type T04 = Foo1<`*${string}*`>;
type T05 = Foo1<`*${number}*`>;
type T06 = Foo1<`*${bigint}*`>;
type T07 = Foo1<`*${any}*`>;
type T08 = Foo1<`**${string}**`>;
type T09 = Foo1<`**${string}**${string}**`>;
type T10 = Foo1<`**${'a' | 'b' | 'c'}**`>;
type T11 = Foo1<`**${boolean}**${boolean}**`>;
declare function foo1<V extends string>(arg: `*${V}*`): V;
function f1<T extends string>(s: string, n: number, b: boolean, t: T) {
let x1 = foo1('hello'); // Error
let x2 = foo1('*hello*');
let x3 = foo1('**hello**');
let x4 = foo1(`*${s}*` as const);
let x5 = foo1(`*${n}*` as const);
let x6 = foo1(`*${b}*` as const);
let x7 = foo1(`*${t}*` as const);
let x8 = foo1(`**${s}**` as const);
}
// Inference to a placeholder immediately followed by another placeholder infers a single
// character or placeholder from the source.
type Parts<T> =
T extends '' ? [] :
T extends `${infer Head}${infer Tail}` ? [Head, ...Parts<Tail>] :
never;
type T20 = Parts<`abc`>;
type T21 = Parts<`*${string}*`>;
type T22 = Parts<`*${number}*`>;
type T23 = Parts<`*${number}*${string}*${bigint}*`>;
function f2() {
let x: `${number}.${number}.${number}`;
x = '1.1.1';
x = '1.1.1' as `1.1.${number}`;
x = '1.1.1' as `1.${number}.1`;
x = '1.1.1' as `1.${number}.${number}`;
x = '1.1.1' as `${number}.1.1`;
x = '1.1.1' as `${number}.1.${number}`;
x = '1.1.1' as `${number}.${number}.1`;
x = '1.1.1' as `${number}.${number}.${number}`;
}
function f3<T extends string>(s: string, n: number, b: boolean, t: T) {
let x: `*${string}*`;
x = 'hello'; // Error
x = '*hello*';
x = '**hello**';
x = `*${s}*` as const;
x = `*${n}*` as const;
x = `*${b}*` as const;
x = `*${t}*` as const;
x = `**${s}**` as const;
}
function f4<T extends number>(s: string, n: number, b: boolean, t: T) {
let x: `*${number}*`;
x = '123'; // Error
x = '*123*';
x = '**123**'; // Error
x = `*${s}*` as const; // Error
x = `*${n}*` as const;
x = `*${b}*` as const; // Error
x = `*${t}*` as const;
}
// Repro from #43060
type A<T> = T extends `${infer U}.${infer V}` ? U | V : never
type B = A<`test.1024`>; // "test" | "1024"
type C = A<`test.${number}`>; // "test" | `${number}`
type D<T> = T extends `${infer U}.${number}` ? U : never
type E = D<`test.1024`>; // "test"
type F = D<`test.${number}`>; // "test"
type G<T> = T extends `${infer U}.${infer V}` ? U | V : never
type H = G<`test.hoge`>; // "test" | "hoge"
type I = G<`test.${string}`>; // string ("test" | string reduces to string)
type J<T> = T extends `${infer U}.${string}` ? U : never
type K = J<`test.hoge`>; // "test"
type L = J<`test.${string}`>; // "test""
// Repro from #43243
type Templated = `${string} ${string}`;
const value1: string = "abc";
const templated1: Templated = `${value1} abc` as const;
// Type '`${string} abc`' is not assignable to type '`${string} ${string}`'.
const value2 = "abc";
const templated2: Templated = `${value2} abc` as const;