Skip to content

Cannot use differentiating property with generics and inheritance #44733

Closed
@marikaner

Description

@marikaner

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

⏯ 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>

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions