Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Template literal types for contextually typed template literal expressions #43376

Merged
merged 4 commits into from
Mar 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31799,7 +31799,12 @@ namespace ts {
texts.push(span.literal.text);
types.push(isTypeAssignableTo(type, templateConstraintType) ? type : stringType);
}
return isConstContext(node) ? getTemplateLiteralType(texts, types) : stringType;
return isConstContext(node) || someType(getContextualType(node) || unknownType, isTemplateLiteralContextualType) ? getTemplateLiteralType(texts, types) : stringType;
}

function isTemplateLiteralContextualType(type: Type): boolean {
return !!(type.flags & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral) ||
type.flags & TypeFlags.InstantiableNonPrimitive && maybeTypeOfKind(getBaseConstraintOfType(type) || unknownType, TypeFlags.StringLike));
}

function getContextNode(node: Expression): Node {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings02.ts(1,5): error TS2322: Type '"AB\nC"' is not assignable to type '"AB\r\nC"'.
tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings02.ts(3,5): error TS2322: Type 'string' is not assignable to type '"DE\nF"'.


==== tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings02.ts (2 errors) ====
==== tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings02.ts (1 errors) ====
let abc: "AB\r\nC" = `AB
~~~
!!! error TS2322: Type '"AB\nC"' is not assignable to type '"AB\r\nC"'.
C`;
let de_NEWLINE_f: "DE\nF" = `DE${"\n"}F`;
~~~~~~~~~~~~
!!! error TS2322: Type 'string' is not assignable to type '"DE\nF"'.
let de_NEWLINE_f: "DE\nF" = `DE${"\n"}F`;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ let abc: "AB\r\nC" = `AB
C`;
let de_NEWLINE_f: "DE\nF" = `DE${"\n"}F`;
>de_NEWLINE_f : "DE\nF"
>`DE${"\n"}F` : string
>`DE${"\n"}F` : "DE\nF"
>"\n" : "\n"

2 changes: 1 addition & 1 deletion tests/baselines/reference/templateLiteralTypes1.types
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const createScopedActionType = <S extends string>(scope: S) => <T extends string
><T extends string>(type: T) => `${scope}/${type}` as `${S}/${T}` : <T extends string>(type: T) => `${S}/${T}`
>type : T
>`${scope}/${type}` as `${S}/${T}` : `${S}/${T}`
>`${scope}/${type}` : string
>`${scope}/${type}` : `${S}/${T}`
>scope : S
>type : T

Expand Down
37 changes: 15 additions & 22 deletions tests/baselines/reference/templateLiteralTypes2.errors.txt
Original file line number Diff line number Diff line change
@@ -1,37 +1,22 @@
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(6,11): error TS2322: Type 'string' is not assignable to type '`abc${string}`'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(7,11): error TS2322: Type 'string' is not assignable to type '`abc${number}`'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(8,11): error TS2322: Type 'string' is not assignable to type '"abcfoo" | "abcbar" | "abcbaz"'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(9,11): error TS2322: Type 'string' is not assignable to type '`abc${T}`'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(21,11): error TS2322: Type 'string' is not assignable to type '`abc${string}`'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(23,11): error TS2322: Type 'string' is not assignable to type '`abc${string}`'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(29,11): error TS2322: Type 'string' is not assignable to type '`foo${string}` | `bar${string}`'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(32,11): error TS2322: Type 'string' is not assignable to type '`foo${string}` | `bar${string}` | `baz${string}`'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(43,11): error TS2322: Type 'string' is not assignable to type '`foo${string}`'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(67,9): error TS2322: Type '`foo${number}`' is not assignable to type 'String'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(68,9): error TS2322: Type '`foo${number}`' is not assignable to type 'Object'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(69,9): error TS2322: Type '`foo${number}`' is not assignable to type '{}'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(70,9): error TS2322: Type '`foo${number}`' is not assignable to type '{ length: number; }'.
tests/cases/conformance/types/literal/templateLiteralTypes2.ts(98,7): error TS2322: Type 'string' is not assignable to type '`${number}px`'.


==== tests/cases/conformance/types/literal/templateLiteralTypes2.ts (14 errors) ====
==== tests/cases/conformance/types/literal/templateLiteralTypes2.ts (7 errors) ====
function ft1<T extends string>(s: string, n: number, u: 'foo' | 'bar' | 'baz', t: T) {
const c1 = `abc${s}`; // `abc${string}`
const c2 = `abc${n}`; // `abc${number}`
const c3 = `abc${u}`; // "abcfoo" | "abcbar" | "abcbaz"
const c4 = `abc${t}`; // `abc${T}
const d1: `abc${string}` = `abc${s}`;
~~
!!! error TS2322: Type 'string' is not assignable to type '`abc${string}`'.
const d2: `abc${number}` = `abc${n}`;
~~
!!! error TS2322: Type 'string' is not assignable to type '`abc${number}`'.
const d3: `abc${'foo' | 'bar' | 'baz'}` = `abc${u}`;
~~
!!! error TS2322: Type 'string' is not assignable to type '"abcfoo" | "abcbar" | "abcbaz"'.
const d4: `abc${T}` = `abc${t}`;
~~
!!! error TS2322: Type 'string' is not assignable to type '`abc${T}`'.
}

function ft2(s: string) {
Expand All @@ -44,8 +29,6 @@ tests/cases/conformance/types/literal/templateLiteralTypes2.ts(98,7): error TS23
const c2 = c1; // Widening type `abc${string}`
let v2 = c2; // Type string
const c3: `abc${string}` = `abc${s}`;
~~
!!! error TS2322: Type 'string' is not assignable to type '`abc${string}`'.
let v3 = c3; // Type `abc${string}`
const c4: `abc${string}` = c1; // Type `abc${string}`
~~
Expand Down Expand Up @@ -74,8 +57,6 @@ tests/cases/conformance/types/literal/templateLiteralTypes2.ts(98,7): error TS23
const c1 = `foo${s}`;
let v1 = c1;
const c2: `foo${string}` = `foo${s}`;
~~
!!! error TS2322: Type 'string' is not assignable to type '`foo${string}`'.
let v2 = c2;
const c3 = `foo${s}` as `foo${string}`;
let v3 = c3;
Expand Down Expand Up @@ -113,6 +94,14 @@ tests/cases/conformance/types/literal/templateLiteralTypes2.ts(98,7): error TS23
!!! error TS2322: Type '`foo${number}`' is not assignable to type '{ length: number; }'.
}

declare function g1<T>(x: T): T;
declare function g2<T extends string>(x: T): T;

function ft20(s: string) {
let x1 = g1(`xyz-${s}`); // string
let x2 = g2(`xyz-${s}`); // `xyz-${string}`
}

// Repro from #41631

declare function takesLiteral<T extends string>(literal: T): T extends `foo.bar.${infer R}` ? R : unknown;
Expand All @@ -139,6 +128,10 @@ tests/cases/conformance/types/literal/templateLiteralTypes2.ts(98,7): error TS23
const pixelString: PixelValueType = `22px`;

const pixelStringWithTemplate: PixelValueType = `${pixelValue}px`;
~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2322: Type 'string' is not assignable to type '`${number}px`'.

// Repro from #43143

function getCardTitle(title: string): `test-${string}` {
return `test-${title}`;
}

30 changes: 28 additions & 2 deletions tests/baselines/reference/templateLiteralTypes2.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ function ft14(t: `foo${number}`) {
let x6: { length: number } = t;
}

declare function g1<T>(x: T): T;
declare function g2<T extends string>(x: T): T;

function ft20(s: string) {
let x1 = g1(`xyz-${s}`); // string
let x2 = g2(`xyz-${s}`); // `xyz-${string}`
}

// Repro from #41631

declare function takesLiteral<T extends string>(literal: T): T extends `foo.bar.${infer R}` ? R : unknown;
Expand All @@ -97,6 +105,12 @@ type PixelValueType = `${number}px`;
const pixelString: PixelValueType = `22px`;

const pixelStringWithTemplate: PixelValueType = `${pixelValue}px`;

// Repro from #43143

function getCardTitle(title: string): `test-${string}` {
return `test-${title}`;
}


//// [templateLiteralTypes2.js]
Expand Down Expand Up @@ -161,6 +175,10 @@ function ft14(t) {
var x4 = t;
var x6 = t;
}
function ft20(s) {
var x1 = g1("xyz-" + s); // string
var x2 = g2("xyz-" + s); // `xyz-${string}`
}
var t1 = takesLiteral("foo.bar.baz"); // "baz"
var id2 = "foo.bar.baz";
var t2 = takesLiteral(id2); // "baz"
Expand All @@ -172,6 +190,10 @@ var t5 = takesLiteral("foo.bar." + someUnion); // "abc" | "def" | "ghi"
var pixelValue = 22;
var pixelString = "22px";
var pixelStringWithTemplate = pixelValue + "px";
// Repro from #43143
function getCardTitle(title) {
return "test-" + title;
}


//// [templateLiteralTypes2.d.ts]
Expand All @@ -185,17 +207,21 @@ declare function nonWidening<T extends string | number | symbol>(x: T): T;
declare function ft13(s: string, cond: boolean): void;
declare type T0 = string | `${number}px`;
declare function ft14(t: `foo${number}`): void;
declare function g1<T>(x: T): T;
declare function g2<T extends string>(x: T): T;
declare function ft20(s: string): void;
declare function takesLiteral<T extends string>(literal: T): T extends `foo.bar.${infer R}` ? R : unknown;
declare const t1: "baz";
declare const id2 = "foo.bar.baz";
declare const t2: "baz";
declare const someString: string;
declare const t3: unknown;
declare const t3: string;
declare const id4: string;
declare const t4: unknown;
declare const someUnion: 'abc' | 'def' | 'ghi';
declare const t5: unknown;
declare const t5: "abc" | "def" | "ghi";
declare const pixelValue: number;
declare type PixelValueType = `${number}px`;
declare const pixelString: PixelValueType;
declare const pixelStringWithTemplate: PixelValueType;
declare function getCardTitle(title: string): `test-${string}`;
105 changes: 72 additions & 33 deletions tests/baselines/reference/templateLiteralTypes2.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -257,68 +257,107 @@ function ft14(t: `foo${number}`) {
>t : Symbol(t, Decl(templateLiteralTypes2.ts, 64, 14))
}

declare function g1<T>(x: T): T;
>g1 : Symbol(g1, Decl(templateLiteralTypes2.ts, 70, 1))
>T : Symbol(T, Decl(templateLiteralTypes2.ts, 72, 20))
>x : Symbol(x, Decl(templateLiteralTypes2.ts, 72, 23))
>T : Symbol(T, Decl(templateLiteralTypes2.ts, 72, 20))
>T : Symbol(T, Decl(templateLiteralTypes2.ts, 72, 20))

declare function g2<T extends string>(x: T): T;
>g2 : Symbol(g2, Decl(templateLiteralTypes2.ts, 72, 32))
>T : Symbol(T, Decl(templateLiteralTypes2.ts, 73, 20))
>x : Symbol(x, Decl(templateLiteralTypes2.ts, 73, 38))
>T : Symbol(T, Decl(templateLiteralTypes2.ts, 73, 20))
>T : Symbol(T, Decl(templateLiteralTypes2.ts, 73, 20))

function ft20(s: string) {
>ft20 : Symbol(ft20, Decl(templateLiteralTypes2.ts, 73, 47))
>s : Symbol(s, Decl(templateLiteralTypes2.ts, 75, 14))

let x1 = g1(`xyz-${s}`); // string
>x1 : Symbol(x1, Decl(templateLiteralTypes2.ts, 76, 7))
>g1 : Symbol(g1, Decl(templateLiteralTypes2.ts, 70, 1))
>s : Symbol(s, Decl(templateLiteralTypes2.ts, 75, 14))

let x2 = g2(`xyz-${s}`); // `xyz-${string}`
>x2 : Symbol(x2, Decl(templateLiteralTypes2.ts, 77, 7))
>g2 : Symbol(g2, Decl(templateLiteralTypes2.ts, 72, 32))
>s : Symbol(s, Decl(templateLiteralTypes2.ts, 75, 14))
}

// Repro from #41631

declare function takesLiteral<T extends string>(literal: T): T extends `foo.bar.${infer R}` ? R : unknown;
>takesLiteral : Symbol(takesLiteral, Decl(templateLiteralTypes2.ts, 70, 1))
>T : Symbol(T, Decl(templateLiteralTypes2.ts, 74, 30))
>literal : Symbol(literal, Decl(templateLiteralTypes2.ts, 74, 48))
>T : Symbol(T, Decl(templateLiteralTypes2.ts, 74, 30))
>T : Symbol(T, Decl(templateLiteralTypes2.ts, 74, 30))
>R : Symbol(R, Decl(templateLiteralTypes2.ts, 74, 87))
>R : Symbol(R, Decl(templateLiteralTypes2.ts, 74, 87))
>takesLiteral : Symbol(takesLiteral, Decl(templateLiteralTypes2.ts, 78, 1))
>T : Symbol(T, Decl(templateLiteralTypes2.ts, 82, 30))
>literal : Symbol(literal, Decl(templateLiteralTypes2.ts, 82, 48))
>T : Symbol(T, Decl(templateLiteralTypes2.ts, 82, 30))
>T : Symbol(T, Decl(templateLiteralTypes2.ts, 82, 30))
>R : Symbol(R, Decl(templateLiteralTypes2.ts, 82, 87))
>R : Symbol(R, Decl(templateLiteralTypes2.ts, 82, 87))

const t1 = takesLiteral("foo.bar.baz"); // "baz"
>t1 : Symbol(t1, Decl(templateLiteralTypes2.ts, 76, 5))
>takesLiteral : Symbol(takesLiteral, Decl(templateLiteralTypes2.ts, 70, 1))
>t1 : Symbol(t1, Decl(templateLiteralTypes2.ts, 84, 5))
>takesLiteral : Symbol(takesLiteral, Decl(templateLiteralTypes2.ts, 78, 1))

const id2 = "foo.bar.baz";
>id2 : Symbol(id2, Decl(templateLiteralTypes2.ts, 77, 5))
>id2 : Symbol(id2, Decl(templateLiteralTypes2.ts, 85, 5))

const t2 = takesLiteral(id2); // "baz"
>t2 : Symbol(t2, Decl(templateLiteralTypes2.ts, 78, 5))
>takesLiteral : Symbol(takesLiteral, Decl(templateLiteralTypes2.ts, 70, 1))
>id2 : Symbol(id2, Decl(templateLiteralTypes2.ts, 77, 5))
>t2 : Symbol(t2, Decl(templateLiteralTypes2.ts, 86, 5))
>takesLiteral : Symbol(takesLiteral, Decl(templateLiteralTypes2.ts, 78, 1))
>id2 : Symbol(id2, Decl(templateLiteralTypes2.ts, 85, 5))

declare const someString: string;
>someString : Symbol(someString, Decl(templateLiteralTypes2.ts, 80, 13))
>someString : Symbol(someString, Decl(templateLiteralTypes2.ts, 88, 13))

const t3 = takesLiteral(`foo.bar.${someString}`); // string
>t3 : Symbol(t3, Decl(templateLiteralTypes2.ts, 81, 5))
>takesLiteral : Symbol(takesLiteral, Decl(templateLiteralTypes2.ts, 70, 1))
>someString : Symbol(someString, Decl(templateLiteralTypes2.ts, 80, 13))
>t3 : Symbol(t3, Decl(templateLiteralTypes2.ts, 89, 5))
>takesLiteral : Symbol(takesLiteral, Decl(templateLiteralTypes2.ts, 78, 1))
>someString : Symbol(someString, Decl(templateLiteralTypes2.ts, 88, 13))

const id4 = `foo.bar.${someString}`;
>id4 : Symbol(id4, Decl(templateLiteralTypes2.ts, 83, 5))
>someString : Symbol(someString, Decl(templateLiteralTypes2.ts, 80, 13))
>id4 : Symbol(id4, Decl(templateLiteralTypes2.ts, 91, 5))
>someString : Symbol(someString, Decl(templateLiteralTypes2.ts, 88, 13))

const t4 = takesLiteral(id4); // string
>t4 : Symbol(t4, Decl(templateLiteralTypes2.ts, 84, 5))
>takesLiteral : Symbol(takesLiteral, Decl(templateLiteralTypes2.ts, 70, 1))
>id4 : Symbol(id4, Decl(templateLiteralTypes2.ts, 83, 5))
>t4 : Symbol(t4, Decl(templateLiteralTypes2.ts, 92, 5))
>takesLiteral : Symbol(takesLiteral, Decl(templateLiteralTypes2.ts, 78, 1))
>id4 : Symbol(id4, Decl(templateLiteralTypes2.ts, 91, 5))

declare const someUnion: 'abc' | 'def' | 'ghi';
>someUnion : Symbol(someUnion, Decl(templateLiteralTypes2.ts, 86, 13))
>someUnion : Symbol(someUnion, Decl(templateLiteralTypes2.ts, 94, 13))

const t5 = takesLiteral(`foo.bar.${someUnion}`); // "abc" | "def" | "ghi"
>t5 : Symbol(t5, Decl(templateLiteralTypes2.ts, 87, 5))
>takesLiteral : Symbol(takesLiteral, Decl(templateLiteralTypes2.ts, 70, 1))
>someUnion : Symbol(someUnion, Decl(templateLiteralTypes2.ts, 86, 13))
>t5 : Symbol(t5, Decl(templateLiteralTypes2.ts, 95, 5))
>takesLiteral : Symbol(takesLiteral, Decl(templateLiteralTypes2.ts, 78, 1))
>someUnion : Symbol(someUnion, Decl(templateLiteralTypes2.ts, 94, 13))

// Repro from #41732

const pixelValue: number = 22;
>pixelValue : Symbol(pixelValue, Decl(templateLiteralTypes2.ts, 91, 5))
>pixelValue : Symbol(pixelValue, Decl(templateLiteralTypes2.ts, 99, 5))

type PixelValueType = `${number}px`;
>PixelValueType : Symbol(PixelValueType, Decl(templateLiteralTypes2.ts, 91, 30))
>PixelValueType : Symbol(PixelValueType, Decl(templateLiteralTypes2.ts, 99, 30))

const pixelString: PixelValueType = `22px`;
>pixelString : Symbol(pixelString, Decl(templateLiteralTypes2.ts, 95, 5))
>PixelValueType : Symbol(PixelValueType, Decl(templateLiteralTypes2.ts, 91, 30))
>pixelString : Symbol(pixelString, Decl(templateLiteralTypes2.ts, 103, 5))
>PixelValueType : Symbol(PixelValueType, Decl(templateLiteralTypes2.ts, 99, 30))

const pixelStringWithTemplate: PixelValueType = `${pixelValue}px`;
>pixelStringWithTemplate : Symbol(pixelStringWithTemplate, Decl(templateLiteralTypes2.ts, 97, 5))
>PixelValueType : Symbol(PixelValueType, Decl(templateLiteralTypes2.ts, 91, 30))
>pixelValue : Symbol(pixelValue, Decl(templateLiteralTypes2.ts, 91, 5))
>pixelStringWithTemplate : Symbol(pixelStringWithTemplate, Decl(templateLiteralTypes2.ts, 105, 5))
>PixelValueType : Symbol(PixelValueType, Decl(templateLiteralTypes2.ts, 99, 30))
>pixelValue : Symbol(pixelValue, Decl(templateLiteralTypes2.ts, 99, 5))

// Repro from #43143

function getCardTitle(title: string): `test-${string}` {
>getCardTitle : Symbol(getCardTitle, Decl(templateLiteralTypes2.ts, 105, 66))
>title : Symbol(title, Decl(templateLiteralTypes2.ts, 109, 22))

return `test-${title}`;
>title : Symbol(title, Decl(templateLiteralTypes2.ts, 109, 22))
}

Loading