-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Erase type parameters to a type which behaves as never in a union and unknown in an intersection or any otherwise #39217
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
Changes from all 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 |
---|---|---|
@@ -1,3 +1,6 @@ | ||
tests/cases/compiler/immutable.ts(189,20): error TS2430: Interface 'Stack<T>' incorrectly extends interface 'Indexed<T>'. | ||
The types returned by 'concat(...).map(...).filter(...)' are incompatible between these types. | ||
Type 'Set<any>' is not assignable to type 'Indexed<any>'. | ||
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. This is just a new follow-on error to the existing errors below, I believe, as the incorrect extensions below make |
||
tests/cases/compiler/immutable.ts(341,22): error TS2430: Interface 'Keyed<K, V>' incorrectly extends interface 'Collection<K, V>'. | ||
The types returned by 'toSeq()' are incompatible between these types. | ||
Type 'Keyed<K, V>' is not assignable to type 'this'. | ||
|
@@ -33,7 +36,7 @@ tests/cases/compiler/immutable.ts(391,22): error TS2430: Interface 'Set<T>' inco | |
flatMap<M>(mapper: (value: T, key: void, iter: this) => Ara<M>, context?: any): N2<M>; | ||
toSeq(): N2<T>; | ||
} | ||
==== tests/cases/compiler/immutable.ts (3 errors) ==== | ||
==== tests/cases/compiler/immutable.ts (4 errors) ==== | ||
// Test that complex recursive collections can pass the `extends` assignability check without | ||
// running out of memory. This bug was exposed in Typescript 2.4 when more generic signatures | ||
// started being checked. | ||
|
@@ -223,6 +226,10 @@ tests/cases/compiler/immutable.ts(391,22): error TS2430: Interface 'Set<T>' inco | |
export function Stack<T>(): Stack<T>; | ||
export function Stack<T>(collection: Iterable<T>): Stack<T>; | ||
export interface Stack<T> extends Collection.Indexed<T> { | ||
~~~~~ | ||
!!! error TS2430: Interface 'Stack<T>' incorrectly extends interface 'Indexed<T>'. | ||
!!! error TS2430: The types returned by 'concat(...).map(...).filter(...)' are incompatible between these types. | ||
!!! error TS2430: Type 'Set<any>' is not assignable to type 'Indexed<any>'. | ||
// Reading values | ||
peek(): T | undefined; | ||
// Persistent changes | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
//// [functionInferenceDecomposesIntersection.ts] | ||
declare namespace React { | ||
type WeakValidationMap<T> = { | ||
[K in keyof T]?: null extends T[K] ? string : string | ||
}; | ||
|
||
interface FunctionComponent<P = {}> { | ||
propTypes?: WeakValidationMap<P>; | ||
} | ||
} | ||
|
||
type A<T1> = <T2>() => React.FunctionComponent<T1 & T2>; | ||
|
||
function B<T>(_: A<T>) {} | ||
|
||
interface C { | ||
r: string; | ||
} | ||
|
||
function myFunction<T2>(): React.FunctionComponent<C & T2> { | ||
return {}; | ||
} | ||
|
||
// B<C>(myFunction) // No error | ||
B(myFunction) // should be the same as above (T in B inferred as C) | ||
|
||
//// [functionInferenceDecomposesIntersection.js] | ||
function B(_) { } | ||
function myFunction() { | ||
return {}; | ||
} | ||
// B<C>(myFunction) // No error | ||
B(myFunction); // should be the same as above (T in B inferred as C) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
=== tests/cases/compiler/functionInferenceDecomposesIntersection.ts === | ||
declare namespace React { | ||
>React : Symbol(React, Decl(functionInferenceDecomposesIntersection.ts, 0, 0)) | ||
|
||
type WeakValidationMap<T> = { | ||
>WeakValidationMap : Symbol(WeakValidationMap, Decl(functionInferenceDecomposesIntersection.ts, 0, 25)) | ||
>T : Symbol(T, Decl(functionInferenceDecomposesIntersection.ts, 1, 27)) | ||
|
||
[K in keyof T]?: null extends T[K] ? string : string | ||
>K : Symbol(K, Decl(functionInferenceDecomposesIntersection.ts, 2, 9)) | ||
>T : Symbol(T, Decl(functionInferenceDecomposesIntersection.ts, 1, 27)) | ||
>T : Symbol(T, Decl(functionInferenceDecomposesIntersection.ts, 1, 27)) | ||
>K : Symbol(K, Decl(functionInferenceDecomposesIntersection.ts, 2, 9)) | ||
|
||
}; | ||
|
||
interface FunctionComponent<P = {}> { | ||
>FunctionComponent : Symbol(FunctionComponent, Decl(functionInferenceDecomposesIntersection.ts, 3, 6)) | ||
>P : Symbol(P, Decl(functionInferenceDecomposesIntersection.ts, 5, 32)) | ||
|
||
propTypes?: WeakValidationMap<P>; | ||
>propTypes : Symbol(FunctionComponent.propTypes, Decl(functionInferenceDecomposesIntersection.ts, 5, 41)) | ||
>WeakValidationMap : Symbol(WeakValidationMap, Decl(functionInferenceDecomposesIntersection.ts, 0, 25)) | ||
>P : Symbol(P, Decl(functionInferenceDecomposesIntersection.ts, 5, 32)) | ||
} | ||
} | ||
|
||
type A<T1> = <T2>() => React.FunctionComponent<T1 & T2>; | ||
>A : Symbol(A, Decl(functionInferenceDecomposesIntersection.ts, 8, 1)) | ||
>T1 : Symbol(T1, Decl(functionInferenceDecomposesIntersection.ts, 10, 7)) | ||
>T2 : Symbol(T2, Decl(functionInferenceDecomposesIntersection.ts, 10, 14)) | ||
>React : Symbol(React, Decl(functionInferenceDecomposesIntersection.ts, 0, 0)) | ||
>FunctionComponent : Symbol(React.FunctionComponent, Decl(functionInferenceDecomposesIntersection.ts, 3, 6)) | ||
>T1 : Symbol(T1, Decl(functionInferenceDecomposesIntersection.ts, 10, 7)) | ||
>T2 : Symbol(T2, Decl(functionInferenceDecomposesIntersection.ts, 10, 14)) | ||
|
||
function B<T>(_: A<T>) {} | ||
>B : Symbol(B, Decl(functionInferenceDecomposesIntersection.ts, 10, 56)) | ||
>T : Symbol(T, Decl(functionInferenceDecomposesIntersection.ts, 12, 11)) | ||
>_ : Symbol(_, Decl(functionInferenceDecomposesIntersection.ts, 12, 14)) | ||
>A : Symbol(A, Decl(functionInferenceDecomposesIntersection.ts, 8, 1)) | ||
>T : Symbol(T, Decl(functionInferenceDecomposesIntersection.ts, 12, 11)) | ||
|
||
interface C { | ||
>C : Symbol(C, Decl(functionInferenceDecomposesIntersection.ts, 12, 25)) | ||
|
||
r: string; | ||
>r : Symbol(C.r, Decl(functionInferenceDecomposesIntersection.ts, 14, 13)) | ||
} | ||
|
||
function myFunction<T2>(): React.FunctionComponent<C & T2> { | ||
>myFunction : Symbol(myFunction, Decl(functionInferenceDecomposesIntersection.ts, 16, 1)) | ||
>T2 : Symbol(T2, Decl(functionInferenceDecomposesIntersection.ts, 18, 20)) | ||
>React : Symbol(React, Decl(functionInferenceDecomposesIntersection.ts, 0, 0)) | ||
>FunctionComponent : Symbol(React.FunctionComponent, Decl(functionInferenceDecomposesIntersection.ts, 3, 6)) | ||
>C : Symbol(C, Decl(functionInferenceDecomposesIntersection.ts, 12, 25)) | ||
>T2 : Symbol(T2, Decl(functionInferenceDecomposesIntersection.ts, 18, 20)) | ||
|
||
return {}; | ||
} | ||
|
||
// B<C>(myFunction) // No error | ||
B(myFunction) // should be the same as above (T in B inferred as C) | ||
>B : Symbol(B, Decl(functionInferenceDecomposesIntersection.ts, 10, 56)) | ||
>myFunction : Symbol(myFunction, Decl(functionInferenceDecomposesIntersection.ts, 16, 1)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
=== tests/cases/compiler/functionInferenceDecomposesIntersection.ts === | ||
declare namespace React { | ||
type WeakValidationMap<T> = { | ||
>WeakValidationMap : WeakValidationMap<T> | ||
|
||
[K in keyof T]?: null extends T[K] ? string : string | ||
>null : null | ||
|
||
}; | ||
|
||
interface FunctionComponent<P = {}> { | ||
propTypes?: WeakValidationMap<P>; | ||
>propTypes : WeakValidationMap<P> | ||
} | ||
} | ||
|
||
type A<T1> = <T2>() => React.FunctionComponent<T1 & T2>; | ||
>A : A<T1> | ||
>React : any | ||
|
||
function B<T>(_: A<T>) {} | ||
>B : <T>(_: A<T>) => void | ||
>_ : A<T> | ||
|
||
interface C { | ||
r: string; | ||
>r : string | ||
} | ||
|
||
function myFunction<T2>(): React.FunctionComponent<C & T2> { | ||
>myFunction : <T2>() => React.FunctionComponent<C & T2> | ||
>React : any | ||
|
||
return {}; | ||
>{} : {} | ||
} | ||
|
||
// B<C>(myFunction) // No error | ||
B(myFunction) // should be the same as above (T in B inferred as C) | ||
>B(myFunction) : void | ||
>B : <T>(_: A<T>) => void | ||
>myFunction : <T2>() => React.FunctionComponent<C & T2> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
|
||
declare namespace React { | ||
type WeakValidationMap<T> = { | ||
[K in keyof T]?: null extends T[K] ? string : string | ||
}; | ||
|
||
interface FunctionComponent<P = {}> { | ||
propTypes?: WeakValidationMap<P>; | ||
} | ||
} | ||
|
||
type A<T1> = <T2>() => React.FunctionComponent<T1 & T2>; | ||
|
||
function B<T>(_: A<T>) {} | ||
|
||
interface C { | ||
r: string; | ||
} | ||
|
||
function myFunction<T2>(): React.FunctionComponent<C & T2> { | ||
return {}; | ||
} | ||
|
||
// B<C>(myFunction) // No error | ||
B(myFunction) // should be the same as above (T in B inferred as C) |
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.
Wow, a schizophrenic type. We have an implicit assumption everywhere that only one of these flags will be set--with a few exceptions such as
TypeFlags.EnumLiteral
. Are you confident there are no places we'll get confused by all three being set in this type? Tests for these now become order dependent.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.
Yep, I'm literally abusing the order dependence of handling each of these to avoid special-casing the handling of this type in most type construction places (partially because we're essentially out of normal type flags). Generally speaking we handle Any, then Never, then Unknown (if we handle both), so it's OK, since this marker is mostly any-like (except in unions/intersections, where we handle unknown or never, and then any, which is what causes the desirable vaporization in both structures). At the start, I also wanted to add
ObjectFlags.NonInferrableType
to it, but that has the side effect of cases like<T extends T[]>() => T
where we currently inferany[]
becomingunknown
. I don't thinkany[]
is great (it injectsany
into the program without the user ever explicitly writing it, and it's not annoImplicitAny
error!), butunknown
isn't really much better. I think the ideal would beunknown[]
- but that relies on knowing thatT[]
is covariant onT
... I guess since we usegetVariances
in inference already, that should be safe. So In theory I could get what I want by mapping this erased type tounknown
in covariant positions andnever
in contravariant positions, when it persists into the constructed type (rather than just vaporizing in a union or intersection and letting it persist beyond that as a psuedo-any
). I could also make this a special type parameter, and handle all this with a special instantiator that tracks some context of the instantiation - in fact, to do the variance bit, I probably need to.