Home > Software design >  TypeScript type for all derived interfaces
TypeScript type for all derived interfaces

Time:04-13

I have an interface inheritence hierarchy like this:

interface IBase { type: stirng; }
interface IDerived1 extends IBase { type: "Derived1"; }
interface IDerived2 extends IBase { type: "Derived2"; }
...

These are actually generated interfaces for C# types, that's why the type discriminator property on them.

Is it possible to wirte a type in TypeScript for all the possible type string literal values,
based on the existing type hierarchy only,
so without specifying them manually one by one?

I would like to get smg like this:

type AllTypesOf<T extends {type: string}> = /* ? what comes here ? */;

...

const myVar: AllTypesOf<IBase>; // "Derived1" | "Derived2" | ...

Which I'd use like this:

function mySwitch(myArg: IBase) {
    const type = myArg.type as AllTypesOf<IBase> // "Derived1" | "Derived2" | ...
    switch(type) {
        case "Derived1":
        case "Derived2":
            // ok

        case "Derived3":
            // NOK, error, does not exist such
    }
}

CodePudding user response:

TypeScript follows basic inheritance rules: parents don't know anything about their children. You could make an Enum of all the values, and then reference them in your types:

enum AllTypesOf {
  DERIVED_1 = "Derived1",
  DERIVED_2 = "Derived2"
}

interface IBase { type: AllTypesOf }

interface IDerived1 extends IBase { type: AllTypesOf.DERIVED_1 }

function mySwitch(myArg: IBase) {
    const type = myArg.type // Automatically typed as AllTypesOf
    switch(type) {
        case AllTypesOf.DERIVED_1:
        case AllTypesOf.DERIVED_2:
            // ok

        case AllTypesOf.DERIVED_3: // error, does not exist in enum
            // NOK, error, does not exist such
    }
}

CodePudding user response:

For this a union type alias would make a lot of sense.
For example, with the types you specified in your question such a union type alias could look like this:

// Your types
interface IBase { type: stirng; }
interface IDerived1 extends IBase { type: "Derived1"; }
interface IDerived2 extends IBase { type: "Derived2"; }
// The new alias:
type ICombined = IDerived1 | IDerived2;

This new type ICombined can then be used as you intended. To get for instance a type entirely made up of the strings that are possibilities for the type field, you can write ICombined["type"].
With the switch statement as an example, the type inference works as well, and trying to use a string that isn't an option for the type field results in a

Type '"some string"' is not comparable to type '"Derived1" | "Derived2"'

compiler error.

  • Related