Home > database >  Infer generic parameter from an array of types
Infer generic parameter from an array of types

Time:04-30

interface Fruit<T extends string> { type: T; }
class Apple implements Fruit<"apple"> { type = "apple"; }
class Orange implements Fruit<"orange"> { type = "orange"; }

const fruitTypes = [ Apple, Orange ];

How can I extract a type that is "apple" | "orange" from fruitTypes?

My best attempt was:

type FruitTypes = InstanceType<typeof fruitTypes[number]> extends Fruit<infer U> ? U : never;

Which unfortunately yields never.

CodePudding user response:

Your attempt yields 'string', not never.

See TS Playground.

I believe the problem lies not in the definition of FruitTypes type, but in definition of your classes.

With your code, TS complains:

(property) Apple.type: string
Property 'type' in type 'Apple' is not assignable to the same property in base type 'Fruit<"apple">'.
  Type 'string' is not assignable to type '"apple"'.

If you change it to:

interface Fruit<T extends string> { type: T; }
class Apple implements Fruit<"apple"> { type: "apple" = "apple"; }
class Orange implements Fruit<"orange"> { type: "orange" = "orange"; }

this error goes away, and FruitTypes is correctly inferred to "apple" | "orange"

TS Playground - corrected code

CodePudding user response:

I think this should solve your problem. We make use of Distributive property of conditional types and infer the type from each of Fruit<T>

interface Fruit<T extends string> { type: T; }

let Apple : Fruit<"apple"> = { type: 'apple' }
let Orange: Fruit<"orange"> = { type: 'orange' }



const fruitTypes = [ Apple, Orange ];

type FruitTypes = 
  | typeof fruitTypes extends Array<infer K> 
    ? K extends any 
      ? K extends Fruit<infer T> 
        ? T 
        : never
      : never 
    : never


// type FruitTypes = "apple" | "orange"

Code Playground

  • Related