[英]Typescript - Enum key values as union types
我也把这段代码放在了 TS playground 中,点击这里,希望对你有帮助。
就像我在animals
object 中所做的那样,使用key in
获取密钥作为enum ANIMALS values
,我找不到一种方法来做同样的事情,但喜欢 function param animalKey
的联合类型。 得到像'cat' | 'lion' | 'parrot' | 'shark' | 'snail'
'cat' | 'lion' | 'parrot' | 'shark' | 'snail'
'cat' | 'lion' | 'parrot' | 'shark' | 'snail'
。
任何帮助将非常感激
enum ANIMALS {
CAT = 'cat',
LION = 'lion',
PARROT = 'parrot',
SHARK = 'shark',
SNAIL = 'snail'
}
interface iBaseAnimal {
name: string,
gender: 'male' | 'female'
wild: boolean
}
interface iShark extends iBaseAnimal {
max_gills: number
}
interface iParrot extends iBaseAnimal {
wing: {
length: 120,
unit: 'cm'
}
}
// DONE Overwritting property when extending base props with Omit
interface iSnail extends Omit<iBaseAnimal, 'gender'> {
gender: 'hermaphrodite'
}
interface iAnimals {
animals: {
// DONE Enum values as key
// PENDING way to interpolate proper interface value base on the enum key
[key in ANIMALS]: iBaseAnimal
},
// PENDING way to get Enum values as union types (similar to [key in ANIMALS] but for function param)
getAnimal: (animalKey:'lion', options: any) => void
}
这是一个可能的解决方案:
enum AnimalsEn {
CAT = "cat",
LION = "lion",
PARROT = "parrot",
SHARK = "shark",
SNAIL = "snail"
}
interface iBaseAnimal {
name: string,
gender: "male" | "female"
wild: boolean
}
type AnimalsExtends = {
[AnimalsEn.SHARK]: {
max_gills: number
},
[AnimalsEn.PARROT]: {
wing: {
length: 120,
unit: "cm"
}
},
[AnimalsEn.SNAIL]: {
gender: "hermaphrodite"
},
}
// Utility to merge two Definition B object properties shadowing A object properties
type $Merge<TA, TB> = Omit<TA, keyof TB> & TB;
// Getting the Base+Extended type of an Animal
type iAnimal<A extends AnimalsEn> =
A extends keyof AnimalsExtends ?
$Merge<iBaseAnimal, AnimalsExtends[A]> :
iBaseAnimal;
interface iAnimals {
animals: {
// PENDING way to interpolate proper interface value base on the enum key
[key in AnimalsEn]: iAnimal<key> // just using utility to get the right Type
},
// Enum as a type ARE union of their posibles values
getAnimal: (animalKey: AnimalsEn, options: any) => void
}
如果您打算稍后添加/删除动物类型,您可以按照以下方式改进此模式,这非常有用:
// a type for AnyAnimal
type AnyAnimal = { [key in AnimalsEn]: iAnimal<key> }[AnimalsEn];
// adding a type property on the finals interfaces
// this way you could easily discriminate animals by their type, refinement works very well with this
type iAnimal<A extends AnimalsEn> =
(A extends keyof AnimalsExtends ?
$Merge<iBaseAnimal, AnimalsExtends[A]> :
iBaseAnimal) & { type: A };
// you could use it very smoothly now :
export class NotExhaustiveError extends Error {
constructor (value: never) {
super(`Not allowed Value[${value}]`);
}
}
function doingSomethingWith(animal: AnyAnimal) {
// here animal is like
// {
// type: AnimalsEn,
// gender: "male" | "female" | "hermaphrodite";
// name: string,
// wild: boolean,
// }
const type = animal.type;
switch (type) {
case AnimalsEn.CAT: // here is a cat!
break;
case AnimalsEn.LION: // here is a lion!
break;
case AnimalsEn.PARROT: // here is a parrot!
break;
case AnimalsEn.SHARK: // here is a shark!
break;
case AnimalsEn.SNAIL: // here is a snail!
break;
default: // this will *throw* at *compile* time if you forgot to handle a case before
throw new NotExhaustiveError(type);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.