Make never inferences with template literal types only in special cases (#46075)

* Make 'never' inferences with template literal types only in special cases

* Accept new baselines

* Add regression test

* Fix comment
This commit is contained in:
Anders Hejlsberg
2021-09-27 06:47:47 -07:00
committed by GitHub
parent 4ce902c5fa
commit 26aef89a72
7 changed files with 85 additions and 7 deletions

View File

@@ -21939,8 +21939,16 @@ namespace ts {
function inferToTemplateLiteralType(source: Type, target: TemplateLiteralType) {
const matches = inferTypesFromTemplateLiteralType(source, target);
const types = target.types;
for (let i = 0; i < types.length; i++) {
inferFromTypes(matches ? matches[i] : neverType, types[i]);
// When the target template literal contains only placeholders (meaning that inference is intended to extract
// single characters and remainder strings) and inference fails to produce matches, we want to infer 'never' for
// each placeholder such that instantiation with the inferred value(s) produces 'never', a type for which an
// assignment check will fail. If we make no inferences, we'll likely end up with the constraint 'string' which,
// upon instantiation, would collapse all the placeholders to just 'string', and an assignment check might
// succeed. That would be a pointless and confusing outcome.
if (matches || every(target.texts, s => s.length === 0)) {
for (let i = 0; i < types.length; i++) {
inferFromTypes(matches ? matches[i] : neverType, types[i]);
}
}
}

View File

@@ -419,7 +419,7 @@ type Foo<T> = T extends `*${infer S}*` ? S : never;
>Foo : Foo<T>
type TF1 = Foo<any>; // never
>TF1 : never
>TF1 : string
type TF2 = Foo<string>; // never
>TF2 : never

View File

@@ -1,4 +1,4 @@
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(20,19): error TS2345: Argument of type '"hello"' is not assignable to parameter of type '`*${string}*`'.
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}*`'.
@@ -29,7 +29,7 @@ tests/cases/conformance/types/literal/templateLiteralTypes3.ts(74,5): error TS23
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'.
!!! error TS2345: Argument of type '"hello"' is not assignable to parameter of type '`*${string}*`'.
let x2 = foo1('*hello*');
let x3 = foo1('**hello**');
let x4 = foo1(`*${s}*` as const);
@@ -138,4 +138,12 @@ tests/cases/conformance/types/literal/templateLiteralTypes3.ts(74,5): error TS23
interface ITest<P extends Prefixes, E extends AllPrefixData = PrefixData<P>> {
blah: string;
}
// Repro from #45906
type Schema = { a: { b: { c: number } } };
declare function chain<F extends keyof Schema>(field: F | `${F}.${F}`): void;
chain("a");

View File

@@ -116,6 +116,14 @@ type PrefixData<P extends Prefixes> = `${P}:baz`;
interface ITest<P extends Prefixes, E extends AllPrefixData = PrefixData<P>> {
blah: string;
}
// Repro from #45906
type Schema = { a: { b: { c: number } } };
declare function chain<F extends keyof Schema>(field: F | `${F}.${F}`): void;
chain("a");
//// [templateLiteralTypes3.js]
@@ -168,6 +176,7 @@ var templated1 = value1 + " abc";
// Type '`${string} abc`' is not assignable to type '`${string} ${string}`'.
var value2 = "abc";
var templated2 = value2 + " abc";
chain("a");
//// [templateLiteralTypes3.d.ts]
@@ -216,3 +225,11 @@ declare type PrefixData<P extends Prefixes> = `${P}:baz`;
interface ITest<P extends Prefixes, E extends AllPrefixData = PrefixData<P>> {
blah: string;
}
declare type Schema = {
a: {
b: {
c: number;
};
};
};
declare function chain<F extends keyof Schema>(field: F | `${F}.${F}`): void;

View File

@@ -385,3 +385,23 @@ interface ITest<P extends Prefixes, E extends AllPrefixData = PrefixData<P>> {
>blah : Symbol(ITest.blah, Decl(templateLiteralTypes3.ts, 114, 78))
}
// Repro from #45906
type Schema = { a: { b: { c: number } } };
>Schema : Symbol(Schema, Decl(templateLiteralTypes3.ts, 116, 1))
>a : Symbol(a, Decl(templateLiteralTypes3.ts, 120, 15))
>b : Symbol(b, Decl(templateLiteralTypes3.ts, 120, 20))
>c : Symbol(c, Decl(templateLiteralTypes3.ts, 120, 25))
declare function chain<F extends keyof Schema>(field: F | `${F}.${F}`): void;
>chain : Symbol(chain, Decl(templateLiteralTypes3.ts, 120, 42))
>F : Symbol(F, Decl(templateLiteralTypes3.ts, 122, 23))
>Schema : Symbol(Schema, Decl(templateLiteralTypes3.ts, 116, 1))
>field : Symbol(field, Decl(templateLiteralTypes3.ts, 122, 47))
>F : Symbol(F, Decl(templateLiteralTypes3.ts, 122, 23))
>F : Symbol(F, Decl(templateLiteralTypes3.ts, 122, 23))
>F : Symbol(F, Decl(templateLiteralTypes3.ts, 122, 23))
chain("a");
>chain : Symbol(chain, Decl(templateLiteralTypes3.ts, 120, 42))

View File

@@ -49,8 +49,8 @@ function f1<T extends string>(s: string, n: number, b: boolean, t: T) {
>t : T
let x1 = foo1('hello'); // Error
>x1 : never
>foo1('hello') : never
>x1 : string
>foo1('hello') : string
>foo1 : <V extends string>(arg: `*${V}*`) => V
>'hello' : "hello"
@@ -379,3 +379,20 @@ interface ITest<P extends Prefixes, E extends AllPrefixData = PrefixData<P>> {
>blah : string
}
// Repro from #45906
type Schema = { a: { b: { c: number } } };
>Schema : Schema
>a : { b: { c: number;}; }
>b : { c: number; }
>c : number
declare function chain<F extends keyof Schema>(field: F | `${F}.${F}`): void;
>chain : <F extends "a">(field: F | `${F}.${F}`) => void
>field : F | `${F}.${F}`
chain("a");
>chain("a") : void
>chain : <F extends "a">(field: F | `${F}.${F}`) => void
>"a" : "a"

View File

@@ -118,3 +118,11 @@ type PrefixData<P extends Prefixes> = `${P}:baz`;
interface ITest<P extends Prefixes, E extends AllPrefixData = PrefixData<P>> {
blah: string;
}
// Repro from #45906
type Schema = { a: { b: { c: number } } };
declare function chain<F extends keyof Schema>(field: F | `${F}.${F}`): void;
chain("a");