簡體   English   中英

Typescript - 枚舉鍵值作為聯合類型

[英]Typescript - Enum key values as union types

我也把這段代碼放在了 TS playground 中,點擊這里,希望對你有幫助。

如何將枚舉鍵值作為聯合類型獲取(即對於 function 參數)

就像我在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM