-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Fix issues + Support template literal types as discriminants #46137
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -124,11 +124,51 @@ type Schema = { a: { b: { c: number } } }; | |
declare function chain<F extends keyof Schema>(field: F | `${F}.${F}`): void; | ||
|
||
chain("a"); | ||
|
||
// Repro from #46125 | ||
|
||
function ff1<T extends string>(x: `foo-${string}`, y: `${string}-bar`, z: `baz-${string}`) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before I forget, can we also get a copy of this test with a signature like function ff1<T extends string>(x: `foo-${T}`, y: `${T}-bar`, z: `baz-${T}`) { ? I figure it should behave the same, but, y'know, coverage. Maybe it can just be more cases on this signature, since it already has a generic parameter for some reason~ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do. |
||
if (x === y) { | ||
x; // `foo-${string}` | ||
} | ||
if (x === z) { // Error | ||
} | ||
} | ||
|
||
function ff2(x: string, y: `foo-${string}` | 'bar') { | ||
if (x === y) { | ||
x; // `foo-${string}` | 'bar' | ||
} | ||
} | ||
|
||
function ff3(x: string, y: `foo-${string}`) { | ||
if (x === 'foo-test') { | ||
x; // 'foo-test' | ||
} | ||
if (y === 'foo-test') { | ||
y; // 'foo-test' | ||
} | ||
} | ||
|
||
// Repro from #46045 | ||
|
||
export type Action = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably want to remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, for sure. |
||
| { type: `${string}_REQUEST` } | ||
| { type: `${string}_SUCCESS`, response: string }; | ||
|
||
export function reducer(action: Action) { | ||
if (action.type === 'FOO_SUCCESS') { | ||
action.type; | ||
action.response; | ||
} | ||
} | ||
|
||
|
||
//// [templateLiteralTypes3.js] | ||
"use strict"; | ||
// Inference from template literal type to template literal type | ||
exports.__esModule = true; | ||
exports.reducer = void 0; | ||
function f1(s, n, b, t) { | ||
var x1 = foo1('hello'); // Error | ||
var x2 = foo1('*hello*'); | ||
|
@@ -177,59 +217,41 @@ var templated1 = value1 + " abc"; | |
var value2 = "abc"; | ||
var templated2 = value2 + " abc"; | ||
chain("a"); | ||
// Repro from #46125 | ||
function ff1(x, y, z) { | ||
if (x === y) { | ||
x; // `foo-${string}` | ||
} | ||
if (x === z) { // Error | ||
} | ||
} | ||
function ff2(x, y) { | ||
if (x === y) { | ||
x; // `foo-${string}` | 'bar' | ||
} | ||
} | ||
function ff3(x, y) { | ||
if (x === 'foo-test') { | ||
x; // 'foo-test' | ||
} | ||
if (y === 'foo-test') { | ||
y; // 'foo-test' | ||
} | ||
} | ||
function reducer(action) { | ||
if (action.type === 'FOO_SUCCESS') { | ||
action.type; | ||
action.response; | ||
} | ||
} | ||
exports.reducer = reducer; | ||
|
||
|
||
//// [templateLiteralTypes3.d.ts] | ||
declare type Foo1<T> = 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<V extends string>(arg: `*${V}*`): V; | ||
declare function f1<T extends string>(s: string, n: number, b: boolean, t: T): void; | ||
declare type Parts<T> = T extends '' ? [] : T extends `${infer Head}${infer Tail}` ? [Head, ...Parts<Tail>] : 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<T extends string>(s: string, n: number, b: boolean, t: T): void; | ||
declare function f4<T extends number>(s: string, n: number, b: boolean, t: T): void; | ||
declare type A<T> = 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> = T extends `${infer U}.${number}` ? U : never; | ||
declare type E = D<`test.1024`>; | ||
declare type F = D<`test.${number}`>; | ||
declare type G<T> = 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> = 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; | ||
declare type Prefixes = "foo" | "bar"; | ||
declare type AllPrefixData = "foo:baz" | "bar:baz"; | ||
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; | ||
}; | ||
}; | ||
export declare type Action = { | ||
type: `${string}_REQUEST`; | ||
} | { | ||
type: `${string}_SUCCESS`; | ||
response: string; | ||
}; | ||
declare function chain<F extends keyof Schema>(field: F | `${F}.${F}`): void; | ||
export declare function reducer(action: Action): void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this flag
foo-${boolean}
andfoo-${number}
as possibly related? We still need to make sure the holes relate when this returnsTrue
, no?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it'll flag them as possibly related. Or, rather, not flag them as definitely unrelated. We could potentially check the placeholders that immediately follow identical prefix texts or immediately precede identical suffix texts, but honestly there's very little gained from it. In fact, probably only the
boolean
vs.number
disqualification. I think what is here already is sufficient, and it impacts whether we flag operands as non-overlapping, which isn't super critical to get 100% right.