-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Superclass/subclass generic type inference produces different results depending on generic usage #39851
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
Comments
@chanderson0,
You can add a default for However you still get
|
Thanks for the clarification; my mistake for using The first case that's confusing to me is that extensions of the generic type don't seem to cause any capturing: class A<T extends number = number> {
foo<U extends T>(u: U): U {
return u;
}
}
class B extends A<1> { }
type AGeneric<T> = T extends A<infer U> ? U : never;
type Ag = AGeneric<A>; // number
type Bg = AGeneric<B>; // number (should be: 1) And then there are several more behaviors that don't seem right. Note how in case 3, there's a whole new type that hasn't shown up anywhere. // ----------------------
// Case 1: captured types
// ----------------------
class A<T extends string | { [k: string]: number } = string> {
_t?: T;
}
class B extends A<{ one: 1 }> { }
type AGeneric<T> = T extends A<infer U> ? U : never;
type Ag = AGeneric<A>; // string
type Bg = AGeneric<B>; // { one: 1 }
// ---------------------
// Case 2: derived types
// ---------------------
type KeysOrString<T> = T extends string ? T : keyof T;
class X<T extends string | { [k: string]: number } = string> {
_u?: KeysOrString<T>;
}
class Y extends X<{ one: 1 }> { }
type XGeneric<T> = T extends X<infer U> ? U : never;
type Xg = XGeneric<X>; // string
type Yg = XGeneric<Y>; // "one" (should be { one: 1 })
// ------------
// Case 3: both
// ------------
class M<T extends string | { [k: string]: number } = string> {
_t?: T;
_u?: KeysOrString<T>;
}
class N extends M<{ one: 1 }> { }
type MGeneric<T> = T extends M<infer U> ? U : never;
type Mg = MGeneric<M>; // string
type Ng = MGeneric<N>; // "one" | { one: 1 } (should be { one: 1 })
// And just to be sure...
type MDefinedGeneric = MGeneric<M<{ one: 1 }>>; // { one: 1 } The original motivation for this was usage of the eventemitter3 types. type Events = {
one: [1],
two: [2]
};
class MyEmitter extends EventEmitter<Events> {
foo() {
this.emit('one', 1);
}
}
type EventEmitterGeneric<T> = T extends EventEmitter<infer U> ? U : never;
type MyEmitterEvents = EventEmitterGeneric<MyEmitter>; // "one" | "two" (should be: Events) Including type definitions: Playground Link |
Look at the FAQ/generics. In the FAQ they only mention to add a member of type I'm not an expert of TS, so my feeling (just my feeling) is that in all the cases you don't specify However, with |
You're observing that generics behave differently depending on their variance, which is the intended behavior. |
This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow. |
TypeScript Version: 3.9.2
Search Terms: generic subclass infer unused
Expected behavior: Inference of generics shouldn't depend on the usage of those generics in the superclass. Separately, the behavior of inferring generics is confusing: sometimes they're left at the base of the superclass, sometimes they're made narrower by subclasses - is this intentional?
Actual behavior: Behavior depends on how the types are used, producing inconsistent results.
Related Issues:
Code
Output
Compiler Options
Playground Link: Provided
The text was updated successfully, but these errors were encountered: