mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-17 21:06:50 -05:00
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:
@@ -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]);
|
||||
|
||||
129
tests/baselines/reference/templateLiteralTypes3.errors.txt
Normal file
129
tests/baselines/reference/templateLiteralTypes3.errors.txt
Normal 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;
|
||||
|
||||
200
tests/baselines/reference/templateLiteralTypes3.js
Normal file
200
tests/baselines/reference/templateLiteralTypes3.js
Normal 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;
|
||||
344
tests/baselines/reference/templateLiteralTypes3.symbols
Normal file
344
tests/baselines/reference/templateLiteralTypes3.symbols
Normal 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))
|
||||
|
||||
365
tests/baselines/reference/templateLiteralTypes3.types
Normal file
365
tests/baselines/reference/templateLiteralTypes3.types
Normal 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"
|
||||
|
||||
108
tests/cases/conformance/types/literal/templateLiteralTypes3.ts
Normal file
108
tests/cases/conformance/types/literal/templateLiteralTypes3.ts
Normal 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;
|
||||
Reference in New Issue
Block a user