简体   繁体   English

在 TypeScript 接口中使用字符串枚举值作为计算属性键

[英]Use string enum value in TypeScript interface as a computed property key

I want to define a function returns different type of object based on the key I give.我想定义一个函数,根据我给出的键返回不同类型的对象。 It's basically like the trick used here in createElement function它基本上就像在createElement函数中使用的技巧

https://github.com/Microsoft/TypeScript/blob/master/lib/lib.dom.d.ts#L3117 https://github.com/Microsoft/TypeScript/blob/master/lib/lib.dom.d.ts#L3117

However, instead of string literal, I want to use string enum type instead.但是,我想使用字符串枚举类型而不是字符串文字。 So I wrote something like this所以我写了这样的东西

class Dog {}
class Cat {}
class Bird {}

enum Kind {
  Dog = 'Dog',
  Cat = 'Cat',
  Bird = 'Bird'
}

interface KindMap {
  [Kind.Dog]: Dog
  [Kind.Cat]: Cat
  [Kind.Bird]: Bird
}

function getAnimal<K extends keyof KindMap> (key: K): KindMap[K] {
  switch (key) {
    case Kind.Dog:
      return new Dog()
    case Kind.Cat:
      return new Cat()
    case Kind.Bird:
      return new Bird()
  }
}

However, TypeScript seems doesn't like the way I put the enum Kind 's value inside interface as the computed property, it complains但是,TypeScript 似乎不喜欢我将enum Kind的值作为计算属性放在 interface 中的方式,它抱怨

A computed property name in an interface must directly refer to a built-in symbol.

Here comes the question, I already have the constants defined in the enum, I don't like to use string literal, is there a way I can make this works?问题来了,我已经在枚举中定义了常量,我不喜欢使用字符串文字,有什么办法可以使它起作用吗? Which means use the Kind enum's value as the computed property key in the KindMap .这意味着使用 Kind 枚举的值作为KindMap的计算属性键。

Sometimes a simple object with keyof is simpler than an enum in TypeScript:有时,带有keyof的简单对象比 TypeScript 中的枚举更简单:

class Dog { }
class Cat { }

const kindMap = {
    Dog,
    Cat
};

type KindMap = typeof kindMap;

function getAnimalClass<K extends keyof KindMap>(key: K): KindMap[K] {
    return kindMap[key];
}

// These types are inferred correctly
const DogClass = getAnimalClass('Dog'); 
const dog = new DogClass();

const CatClass = getAnimalClass('Cat');
const cat = new CatClass();

Try it in TypeScript Playground 在 TypeScript Playground 中尝试

I tried to implement getAnimal() with mapped types but seemed to run into buggy inference.我尝试使用映射类型实现getAnimal()但似乎遇到了错误的推理。 But looking up the class was easier.但是查找类更容易。

Inlining the getAnimalClass lookup also works with type inference:内联getAnimalClass查找也适用于类型推断:

const dog = new kindMap['Dog']();

The original code almost works as-is in TypeScript 2.7.2, although it does have an error because values other than the 3 Kind s may be passed in.原始代码几乎按原样在 TypeScript 2.7.2 中工作,尽管它确实有错误,因为可能会传入 3 Kind以外的值。

You can say that it's possible to return undefined :你可以说有可能返回undefined

function getAnimal<K extends keyof KindMap>(key: K): undefined | KindMap[K] {
    switch (key) {
        case Kind.Dog:
            return new Dog()
        case Kind.Cat:
            return new Cat()
        case Kind.Bird:
            return new Bird()
        default:
            return undefined;
    }
}

I'd probably just use a map of the enum to the class constructor directly, as using it can easily infer the type:我可能只是直接使用枚举到类构造函数的映射,因为使用它可以轻松推断类型:

const kindMap = {
    [Kind.Dog]: Dog,
    [Kind.Cat]: Cat,
    [Kind.Bird]: Bird,
}

const tweeter = new kindMap[Kind.Bird]();

If you want the function to instantiate the objects, you can also set up a parallel interface:如果你想让函数实例化对象,你也可以设置一个并行接口:

interface KindMap {
    [Kind.Dog]: Dog,
    [Kind.Cat]: Cat,
    [Kind.Bird]: Bird,
}

const getAnimal = <K extends Kind>(k: K): KindMap[K] => new kindMap[k]();

const meower = getAnimal(Kind.Cat);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM