【问题标题】:Casting strings to enum in TypeScript在 TypeScript 中将字符串转换为枚举
【发布时间】:2019-03-10 17:42:46
【问题描述】:

在 TypeScript 中使用枚举时遇到了一个小问题。我的情况是这样的:

  • 我已经定义了一个包含允许值的字符串枚举
  • 我定义了一个接受任何传入值(string 类型)的方法,并且必须将其强制转换为所述枚举

问题是,即使在检查了方法传入的value 之后,intellisense 告诉我value 仍然是string 的类型而不是枚举。如何强制 value 成为 AllowedValues 的类型?

这是一个概念验证示例:

/** enum */
enum AllowedValues {
    LOREM_IPSUM = 'lorem ipsum',
    DOLOR_SIT = 'dolor sir',
    AMET = 'amet'
}

/** @method */
function doSomething(value: string = AllowedValues.LOREM_IPSUM) {

    // If value is not found in enum, force it to a default
    if (!(Object as any).values(AllowedValues).includes(value))
        value = AllowedValues.LOREM_IPSUM;

    // Value should be of type `AllowedValues` here
    // But TypeScript/Intellisense still thinks it is `string`
    console.log(value);
}

doSomething('amet');    // Should log `amet`
doSomething('aloha');   // Should log `lorem ipsum`, since it is not found in `AllowedValues`

您也可以在 TypeScript playground 上找到它。

【问题讨论】:

  • const av = Object.keys(AllowedValues).find(k => AllowedValues[k] === 'dolor sir') 作为 AllowedValues; console.log(av);

标签: typescript intellisense ecmascript-2017


【解决方案1】:

这里发生了一些事情。一是 TypeScript 不理解 Object.values(x).includes(y)y 上的 type guard。它与编译器尝试缩小类型的内置方式不匹配,例如typeofinstanceofin 检查。为了帮助编译器,您可以使用user-defined type guard 来表达这种检查方式:

function isPropertyValue<T>(object: T, possibleValue: any): possibleValue is T[keyof T] {
  return Object.values(object).includes(possibleValue);
}

declare function onlyAcceptAllowedValues(allowedValue: AllowedValues): void;
declare const v: string;
if (isPropertyValue(AllowedValues, v)) {
  onlyAcceptAllowedValues(v); // v is narrowed to AllowedValues; it works!
}

所以我们先把你的函数改成这样:

function doSomething(value: string = AllowedValues.LOREM_IPSUM) {    
  if (!(isPropertyValue(AllowedValues, value)))
    value = AllowedValues.LOREM_IPSUM;

  // TypeScript/Intellisense still thinks it is `string`
  console.log(value);
}

呃哦,还是不行。


第二件事:如果你重新分配一个变量的值,TypeScript 基本上放弃了它的缩小。编译器付出了相当大的努力来理解控制流对变量类型的影响,但是it's not perfect。因此,尽管我们了解将AllowedValues.LOREM_IPSUM 分配给value 使其成为AllowedValues,但编译器gives up 并假定它是其原始注释类型,即string

处理this的方法是创建一个编译器理解的新变量,除了AllowedValues,它永远不会是任何东西。最直接的方法是将其设为const 变量,如下所示:

function doSomething(value: string = AllowedValues.LOREM_IPSUM) {    
  const allowedValue = isPropertyValue(AllowedValues, value) ? value : AllowedValues.LOREM_IPSUM;
  console.log(allowedValue);
}

在上面,新变量allowedValue 被推断为AllowedValues,因为如果类型保护成功(此时valueAllowedValues),它被设置为value,或者@ 987654344@ 如果类型保护失败。无论哪种方式,allowedValue 都是 AllowedValues


因此,如果您想帮助编译器理解事物,那将是我建议的更改。希望有帮助。祝你好运!

【讨论】:

  • 感谢您提供非常详细的答案!现在这对我来说很有意义:我不知道如果在任何时候发生变量重新分配,TypeSCript 将默认为原始注释类型。
猜你喜欢
  • 2018-07-07
  • 2013-06-27
  • 2017-07-07
  • 2020-10-30
  • 2021-04-11
  • 2010-10-03
  • 1970-01-01
相关资源
最近更新 更多