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

时间:2017-10-19 02:37:59

标签: typescript typescript2.0

我想定义一个函数,根据我给出的键返回不同类型的对象。它基本上就像createElement函数

中使用的技巧一样

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

但是,我想使用字符串枚举类型而不是字符串文字。所以我写了这样的东西

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()
  }
}

但是,TypeScript似乎不喜欢我将enum Kind的值放在接口内作为计算属性的方式,它抱怨

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

问题是,我已经在枚举中定义了常量,我不喜欢使用字符串文字,有没有办法可以使这个有效?这意味着使用Kind枚举值作为KindMap中的计算属性键。

2 个答案:

答案 0 :(得分:1)

有时,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

我尝试使用映射类型实现getAnimal(),但似乎遇到了错误的推理。但是查阅课程更容易。

内联getAnimalClass查找也适用于类型推断:

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

答案 1 :(得分:1)

原始代码几乎在TypeScript 2.7.2中按原样运行,但它确实有错误,因为可以传入3 Kind以外的值。

您可以说可以返回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;
    }
}

我可能只是直接使用枚举的映射到类构造函数,因为使用它可以很容易地推断出类型:

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

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

如果希望函数实例化对象,还可以设置并行接口:

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);