打字稿构造函数:接受枚举字符串

时间:2018-07-20 00:02:32

标签: angular typescript

我正在使用TypeScript为Angular中的模型定义一个构造函数。模型中的属性之一被设置为带有一些可能的字符串值的枚举。如果我将枚举值传递给构造函数,则效果很好。 问题是我需要基于API响应调用构造函数,该API返回一个字符串,该字符串用于需要映射到该属性的值。

有什么方法可以将字符串(只要它是枚举中定义的值之一)传递给构造函数?

枚举:

export enum TestTypes {
    FIRST = 'first',
    SECOND = 'second',
}

export class Test {
    this.myType: TestTypes;

    constructor(options: {type: TestTypes}) {
        this.myType = options.type;
    }
}

以下作品 const a = new Test({type:TestTypes.FIRST});

我想要实现的目标: const b = new Test({type:'first'})

我应该执行以下操作吗? const b = new Test({type:TestTypes['first']})

1 个答案:

答案 0 :(得分:1)

最简单的方法是将enum更改为像这样的直接词典:

const literal = <L extends string | number | boolean>(l: L) => l;

export const TestTypes = {
  FIRST: literal('first'),
  SECOND: literal('second'),
};

export type TestTypes = (typeof TestTypes)[keyof typeof TestTypes]

literal()函数是一个帮助程序,它提示编译器解释值as a string literal而不是扩展为string

现在,值TestTypes.FIRST就是字符串"first",值TestTypes.SECOND就是字符串"second",类型TestTypes就是字符串联合"first"|"second"。这样一来,您的课堂就能按需工作:

export class Test {
  myType: TestTypes; // this is an annotation, not an initializer, right?

  constructor(options: { type: TestTypes }) {
    // options.type, not type  
    this.myType = options.type;
  }
}

const a = new Test({ type: TestTypes.FIRST }); // okay
const b = new Test({ type: "first" }); // okay... it's the same thing

如果您想将TestTypes保留为enum,则可以得到所需的内容,但是我认为这实在是太麻烦了。

首先,如果您想要一个独立的函数来接受enum或正确的string值,则可以这样制作一个generic function

declare function acceptTestTypesOrString<E extends string>(
  k: E & (Extract<TestTypes, E> extends never ? never : E)
): void;

我不知道是否应该解释这一点,但是它利用了TestTypes.FIRST extends "first"这样的事实。让我们看看它是否有效:

acceptTestTypesOrString(TestTypes.FIRST) // okay
acceptTestTypesOrString(TestTypes.SECOND) // okay
acceptTestTypesOrString("first") // okay
acceptTestTypesOrString("second") // okay
acceptTestTypesOrString("third") // error

看起来不错。但是您希望将此作为构造函数。然后您can't make a constructor function generic。相反,您可以使整个类通用,如下所示:

export class Test<E extends string> {
  myType: E; // this is an annotation, not an initializer, right?

  constructor(options: { 
    type: E & (Extract<TestTypes, E> extends never ? never : E) 
  }) {
    // options.type, not type  
    this.myType = options.type;
  }
}

const a = new Test({ type: TestTypes.FIRST }); // okay
const b = new Test({ type: "first" }); // also okay

在这种情况下,a的类型为Test<TestTypes.FIRST>b的类型为Test<"first">。它们通常是可互换的,但是当只希望为构造函数使用整个类的泛型类型时,似乎不是最佳选择。

但是可以。


好的,希望其中一个想法有所帮助。祝你好运!