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 3 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
29 changes: 7 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 @@ -139,6 +120,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}`;
}

15 changes: 13 additions & 2 deletions tests/baselines/reference/templateLiteralTypes2.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,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 @@ -172,6 +178,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 @@ -190,12 +200,13 @@ 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}`;
10 changes: 10 additions & 0 deletions tests/baselines/reference/templateLiteralTypes2.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -322,3 +322,13 @@ const pixelStringWithTemplate: PixelValueType = `${pixelValue}px`;
>PixelValueType : Symbol(PixelValueType, Decl(templateLiteralTypes2.ts, 91, 30))
>pixelValue : Symbol(pixelValue, Decl(templateLiteralTypes2.ts, 91, 5))

// Repro from #43143

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

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

55 changes: 33 additions & 22 deletions tests/baselines/reference/templateLiteralTypes2.types
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,22 @@ function ft1<T extends string>(s: string, n: number, u: 'foo' | 'bar' | 'baz', t

const d1: `abc${string}` = `abc${s}`;
>d1 : `abc${string}`
>`abc${s}` : string
>`abc${s}` : `abc${string}`
>s : string

const d2: `abc${number}` = `abc${n}`;
>d2 : `abc${number}`
>`abc${n}` : string
>`abc${n}` : `abc${number}`
>n : number

const d3: `abc${'foo' | 'bar' | 'baz'}` = `abc${u}`;
>d3 : "abcfoo" | "abcbar" | "abcbaz"
>`abc${u}` : string
>`abc${u}` : "abcfoo" | "abcbar" | "abcbaz"
>u : "foo" | "bar" | "baz"

const d4: `abc${T}` = `abc${t}`;
>d4 : `abc${T}`
>`abc${t}` : string
>`abc${t}` : `abc${T}`
>t : T
}

Expand Down Expand Up @@ -79,7 +79,7 @@ function ft10(s: string) {

const c3: `abc${string}` = `abc${s}`;
>c3 : `abc${string}`
>`abc${s}` : string
>`abc${s}` : `abc${string}`
>s : string

let v3 = c3; // Type `abc${string}`
Expand Down Expand Up @@ -168,7 +168,7 @@ function ft12(s: string) {

const c2: `foo${string}` = `foo${s}`;
>c2 : `foo${string}`
>`foo${s}` : string
>`foo${s}` : `foo${string}`
>s : string

let v2 = c2;
Expand All @@ -178,7 +178,7 @@ function ft12(s: string) {
const c3 = `foo${s}` as `foo${string}`;
>c3 : `foo${string}`
>`foo${s}` as `foo${string}` : `foo${string}`
>`foo${s}` : string
>`foo${s}` : `foo${string}`
>s : string

let v3 = c3;
Expand All @@ -188,7 +188,7 @@ function ft12(s: string) {
const c4 = <`foo${string}`>`foo${s}`;
>c4 : `foo${string}`
><`foo${string}`>`foo${s}` : `foo${string}`
>`foo${s}` : string
>`foo${s}` : `foo${string}`
>s : string

let v4 = c4;
Expand Down Expand Up @@ -237,20 +237,20 @@ function ft13(s: string, cond: boolean) {
>s : string

let y1 = nonWidening(`foo${s}`);
>y1 : string
>nonWidening(`foo${s}`) : string
>y1 : `foo${string}`
>nonWidening(`foo${s}`) : `foo${string}`
>nonWidening : <T extends string | number | symbol>(x: T) => T
>`foo${s}` : string
>`foo${s}` : `foo${string}`
>s : string

let y2 = nonWidening(cond ? 'a' : `foo${s}`);
>y2 : string
>nonWidening(cond ? 'a' : `foo${s}`) : string
>y2 : `foo${string}` | "a"
>nonWidening(cond ? 'a' : `foo${s}`) : `foo${string}` | "a"
>nonWidening : <T extends string | number | symbol>(x: T) => T
>cond ? 'a' : `foo${s}` : string
>cond ? 'a' : `foo${s}` : `foo${string}` | "a"
>cond : boolean
>'a' : "a"
>`foo${s}` : string
>`foo${s}` : `foo${string}`
>s : string
}

Expand Down Expand Up @@ -309,10 +309,10 @@ declare const someString: string;
>someString : string

const t3 = takesLiteral(`foo.bar.${someString}`); // string
>t3 : unknown
>takesLiteral(`foo.bar.${someString}`) : unknown
>t3 : string
>takesLiteral(`foo.bar.${someString}`) : string
>takesLiteral : <T extends string>(literal: T) => T extends `foo.bar.${infer R}` ? R : unknown
>`foo.bar.${someString}` : string
>`foo.bar.${someString}` : `foo.bar.${string}`
>someString : string

const id4 = `foo.bar.${someString}`;
Expand All @@ -330,10 +330,10 @@ declare const someUnion: 'abc' | 'def' | 'ghi';
>someUnion : "abc" | "def" | "ghi"

const t5 = takesLiteral(`foo.bar.${someUnion}`); // "abc" | "def" | "ghi"
>t5 : unknown
>takesLiteral(`foo.bar.${someUnion}`) : unknown
>t5 : "abc" | "def" | "ghi"
>takesLiteral(`foo.bar.${someUnion}`) : "abc" | "def" | "ghi"
>takesLiteral : <T extends string>(literal: T) => T extends `foo.bar.${infer R}` ? R : unknown
>`foo.bar.${someUnion}` : string
>`foo.bar.${someUnion}` : "foo.bar.abc" | "foo.bar.def" | "foo.bar.ghi"
>someUnion : "abc" | "def" | "ghi"

// Repro from #41732
Expand All @@ -351,6 +351,17 @@ const pixelString: PixelValueType = `22px`;

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

// Repro from #43143

function getCardTitle(title: string): `test-${string}` {
>getCardTitle : (title: string) => `test-${string}`
>title : string

return `test-${title}`;
>`test-${title}` : `test-${string}`
>title : string
}

Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,9 @@ 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}`;
}