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"