From 4774666f2343c6ffdbf862ffff63f8653a09449b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 28 Mar 2021 13:11:56 -0700 Subject: [PATCH] Support relations and inference between template literal types (#43361) * Support assignment and inference between template literal types * Add tests * Accept new baselines * Add comments --- src/compiler/checker.ts | 158 +++++--- .../templateLiteralTypes3.errors.txt | 129 +++++++ .../reference/templateLiteralTypes3.js | 200 ++++++++++ .../reference/templateLiteralTypes3.symbols | 344 +++++++++++++++++ .../reference/templateLiteralTypes3.types | 365 ++++++++++++++++++ .../types/literal/templateLiteralTypes3.ts | 108 ++++++ 6 files changed, 1254 insertions(+), 50 deletions(-) create mode 100644 tests/baselines/reference/templateLiteralTypes3.errors.txt create mode 100644 tests/baselines/reference/templateLiteralTypes3.js create mode 100644 tests/baselines/reference/templateLiteralTypes3.symbols create mode 100644 tests/baselines/reference/templateLiteralTypes3.types create mode 100644 tests/cases/conformance/types/literal/templateLiteralTypes3.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c6a2030d497d8..193bb1d8c8099 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14295,9 +14295,7 @@ namespace ts { return type.flags & TypeFlags.StringLiteral ? (type).value : type.flags & TypeFlags.NumberLiteral ? "" + (type).value : type.flags & TypeFlags.BigIntLiteral ? pseudoBigIntToString((type).value) : - type.flags & TypeFlags.BooleanLiteral ? (type).intrinsicName : - type.flags & TypeFlags.Null ? "null" : - type.flags & TypeFlags.Undefined ? "undefined" : + type.flags & (TypeFlags.BooleanLiteral | TypeFlags.Nullable) ? (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)); - } - 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); - } - } - - 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; - } - matches.push(getLiteralType(str.slice(pos))); + function isValidTypeForTemplateLiteralPlaceholder(source: Type, target: Type): boolean { + if (source === target || target.flags & (TypeFlags.Any | TypeFlags.String)) { + return true; + } + if (source.flags & TypeFlags.StringLiteral) { + const value = (source).value; + return !!(target.flags & TypeFlags.Number && value !== "" && isFinite(+value) || + target.flags & TypeFlags.BigInt && value !== "" && isValidBigIntString(value) || + target.flags & (TypeFlags.BooleanLiteral | TypeFlags.Nullable) && value === (target).intrinsicName); + } + if (source.flags & TypeFlags.TemplateLiteral) { + const texts = (source).texts; + return texts.length === 2 && texts[0] === "" && texts[1] === "" && isTypeAssignableTo((source).types[0], target); + } + return isTypeAssignableTo(source, target); + } + + function inferTypesFromTemplateLiteralType(source: Type, target: TemplateLiteralType): Type[] | undefined { + return source.flags & TypeFlags.StringLiteral ? inferFromLiteralPartsToTemplateLiteral([(source).value], emptyArray, target) : + source.flags & TypeFlags.TemplateLiteral ? + arraysEqual((source).texts, target.texts) ? map((source).types, getStringLikeTypeForType) : + inferFromLiteralPartsToTemplateLiteral((source).texts, (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; + } + } + 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(source, target) : - source.flags & TypeFlags.TemplateLiteral && arraysEqual((source).texts, target.texts) ? (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]); diff --git a/tests/baselines/reference/templateLiteralTypes3.errors.txt b/tests/baselines/reference/templateLiteralTypes3.errors.txt new file mode 100644 index 0000000000000..67fef27292ef9 --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypes3.errors.txt @@ -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 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(arg: `*${V}*`): V; + + function f1(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 extends '' ? [] : + T extends `${infer Head}${infer Tail}` ? [Head, ...Parts] : + 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(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(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 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 extends `${infer U}.${number}` ? U : never + type E = D<`test.1024`>; // "test" + type F = D<`test.${number}`>; // "test" + + type G = 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 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; + \ No newline at end of file diff --git a/tests/baselines/reference/templateLiteralTypes3.js b/tests/baselines/reference/templateLiteralTypes3.js new file mode 100644 index 0000000000000..f57d98f980941 --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypes3.js @@ -0,0 +1,200 @@ +//// [templateLiteralTypes3.ts] +// Inference from template literal type to template literal type + +type Foo1 = 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(arg: `*${V}*`): V; + +function f1(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 extends '' ? [] : + T extends `${infer Head}${infer Tail}` ? [Head, ...Parts] : + 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(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(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 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 extends `${infer U}.${number}` ? U : never +type E = D<`test.1024`>; // "test" +type F = D<`test.${number}`>; // "test" + +type G = 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 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 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(arg: `*${V}*`): V; +declare function f1(s: string, n: number, b: boolean, t: T): void; +declare type Parts = T extends '' ? [] : T extends `${infer Head}${infer Tail}` ? [Head, ...Parts] : 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(s: string, n: number, b: boolean, t: T): void; +declare function f4(s: string, n: number, b: boolean, t: T): void; +declare type A = 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 extends `${infer U}.${number}` ? U : never; +declare type E = D<`test.1024`>; +declare type F = D<`test.${number}`>; +declare type G = 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 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; diff --git a/tests/baselines/reference/templateLiteralTypes3.symbols b/tests/baselines/reference/templateLiteralTypes3.symbols new file mode 100644 index 0000000000000..f51bf02e74747 --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypes3.symbols @@ -0,0 +1,344 @@ +=== tests/cases/conformance/types/literal/templateLiteralTypes3.ts === +// Inference from template literal type to template literal type + +type Foo1 = 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(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(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 = +>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] : +>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(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(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 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 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 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 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)) + diff --git a/tests/baselines/reference/templateLiteralTypes3.types b/tests/baselines/reference/templateLiteralTypes3.types new file mode 100644 index 0000000000000..2243b25218abc --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypes3.types @@ -0,0 +1,365 @@ +=== tests/cases/conformance/types/literal/templateLiteralTypes3.ts === +// Inference from template literal type to template literal type + +type Foo1 = T extends `*${infer U}*` ? U : never; +>Foo1 : Foo1 + +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(arg: `*${V}*`): V; +>foo1 : (arg: `*${V}*`) => V +>arg : `*${V}*` + +function f1(s: string, n: number, b: boolean, t: T) { +>f1 : (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 : (arg: `*${V}*`) => V +>'hello' : "hello" + + let x2 = foo1('*hello*'); +>x2 : "hello" +>foo1('*hello*') : "hello" +>foo1 : (arg: `*${V}*`) => V +>'*hello*' : "*hello*" + + let x3 = foo1('**hello**'); +>x3 : "*hello*" +>foo1('**hello**') : "*hello*" +>foo1 : (arg: `*${V}*`) => V +>'**hello**' : "**hello**" + + let x4 = foo1(`*${s}*` as const); +>x4 : string +>foo1(`*${s}*` as const) : string +>foo1 : (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 : (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 : (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 : (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 : (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 = +>Parts : Parts + + T extends '' ? [] : + T extends `${infer Head}${infer Tail}` ? [Head, ...Parts] : + 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(s: string, n: number, b: boolean, t: T) { +>f3 : (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(s: string, n: number, b: boolean, t: T) { +>f4 : (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 extends `${infer U}.${infer V}` ? U | V : never +>A : A + +type B = A<`test.1024`>; // "test" | "1024" +>B : "test" | "1024" + +type C = A<`test.${number}`>; // "test" | `${number}` +>C : `${number}` | "test" + +type D = T extends `${infer U}.${number}` ? U : never +>D : D + +type E = D<`test.1024`>; // "test" +>E : "test" + +type F = D<`test.${number}`>; // "test" +>F : "test" + +type G = T extends `${infer U}.${infer V}` ? U | V : never +>G : G + +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 extends `${infer U}.${string}` ? U : never +>J : J + +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" + diff --git a/tests/cases/conformance/types/literal/templateLiteralTypes3.ts b/tests/cases/conformance/types/literal/templateLiteralTypes3.ts new file mode 100644 index 0000000000000..81ab2427963cd --- /dev/null +++ b/tests/cases/conformance/types/literal/templateLiteralTypes3.ts @@ -0,0 +1,108 @@ +// @strict: true +// @declaration: true + +// Inference from template literal type to template literal type + +type Foo1 = 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(arg: `*${V}*`): V; + +function f1(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 extends '' ? [] : + T extends `${infer Head}${infer Tail}` ? [Head, ...Parts] : + 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(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(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 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 extends `${infer U}.${number}` ? U : never +type E = D<`test.1024`>; // "test" +type F = D<`test.${number}`>; // "test" + +type G = 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 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;