Closed
Description
Bug Report
In my code I want to differentiate allowed types based on a differentiating property (version
in my example).
Some classes have this property set, other (generic) classes should accept certain values based on the classes with this property.
This works, as long as I don't introduce hierarchies between the generic classes.
With hierarchies, only values that relate to all differentiating properties (i.e., common values) can be set (see code below).
Other values fail with:
Argument of type '"a"' is not assignable to parameter of type 'TypeByVersion<VersionOf<T>>'.(2345)
🔎 Search Terms
generics, inheritance
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about (Generic conditional type T extends never ? 'yes' : 'no' resolves to never when T is never. #31751). But this wasn't related. I think.
⏯ Playground Link
Playground link with relevant code
💻 Code
/* Version to differentiate on */
type Version = 'v1' | 'v2';
/* Types by version */
type Common = 'c';
type V1 = 'a';
type V2 = 'b';
type TypeByVersion<T extends 'v1' | 'v2' | 'any'> = T extends 'v1'
? Common | V1
: T extends 'v2'
? Common | V2
: Common | V1 | V2;
/* Classes with a property to differentiate on */
abstract class MyClassBase {
constructor(public name: string) {}
}
// Version 1
abstract class MyClassV1 extends MyClassBase {
readonly version: 'v1' = 'v1';
}
// Version 2
abstract class MyClassV2 extends MyClassBase {
readonly version: 'v2' = 'v2';
}
/* Type to infer the differentiating property */
type VersionOf<T extends MyClassBase> = T extends {
version: infer VersionT;
}
? VersionT
: 'any';
/* Classes using the classes above in generics */
/* Case 1: Without base class, directly referencing MyClassV1 */
class GenericClassV1<T extends MyClassV1> {
constructor(public arg: TypeByVersion<VersionOf<T>>) {}
}
new GenericClassV1('a'); // <----- works as expected ✔
new GenericClassV1('b'); // <----- fails as expected ✔
new GenericClassV1('c'); // <----- works as expected ✔
/* Case 2: With base class, referencing MyClassBase in the base class, but MyClassV1 in the specific class */
class GenericClassBase<T extends MyClassBase> {
constructor(public arg: TypeByVersion<VersionOf<T>>) {}
}
new GenericClassBase('a'); // <----- works as expected ✔
new GenericClassBase('b'); // <----- works as expected ✔
new GenericClassBase('c'); // <----- works as expected ✔
class GenericClassV1Ext<T extends MyClassV1> extends GenericClassBase<T> {
constructor() {
super('a'); // <----- fails, but not expected ❌
super('b'); // <----- fails as expected ✔
super('c'); // <----- works as expected ✔
}
}
🙁 Actual behavior
Calling the super constructor from GenericClassV1Ext
with 'a'
fails.
🙂 Expected behavior
I would expect calling the super constructor from GenericClassV1Ext
with 'a'
works, because it works for both other cases:
GenericClassV1<T extends MyClassV1>
GenericClassBase<T extends MyClassBase>