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

* Support assignment and inference between template literal types

* Add tests

* Accept new baselines

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

View File

@@ -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;