Typescript - 如何检查给定值是否在联合类型数组中

Jør*_*edt 9 typescript

我有一个给定的联合类型的数组,然后想检查数组中是否包含来自联合类型的超集的字符串(运行时检查):

const validOptions: ("foo" | "bar")[] = ["foo", "bar"]
type IArrType = typeof validOptions[number]
const key: IArrType | "alien" = "alien" // Rather: some random function
const isKeyInArr = validOptions.indexOf(key) > -1 // Error: "alien" is not assignable to "foo" | "bar"

// Fix 1:
const isKeyValidCast = validOptions.indexOf(<IArrType>key) > -1 
// Fix 2:
const isKeyValidExplicit = 
      key === "alien" ? false : validOptions.indexOf(key) > -1 // OK: type guard magic
Run Code Online (Sandbox Code Playgroud)

修复1可以,但不是很优雅.修复2愚弄编译器,但是误导和低效的运行时.在我的例子中,"外来"字符串类型只是不在联合类型中的任何字符串的占位符.

有没有什么方法可以在没有强制转换或显式测试的情况下编译?表达式可以否定,以便我们让这个"类型后卫"起作用吗?

顺便说一句:这个非常酷的答案展示了如何从值列表构造一个类型化的元组:Typescript从元组/数组值派生联合类型

jts*_*ven 12

接受的答案使用类型断言/转换,但从评论看来,OP 使用的解决方案使用find了不同的工作方式。我也更喜欢这个解决方案,所以这里是如何工作的:

const configKeys = ['foo', 'bar'] as const;
type ConfigKey = typeof configKeys[number]; // "foo" | "bar"

// Return a typed ConfigKey from a string read at runtime (or throw if invalid).
function getTypedConfigKey(maybeConfigKey: string): ConfigKey {
    const configKey = configKeys.find((validKey) => validKey === maybeConfigKey);
    if (configKey) {
        return configKey;
    }
    throw new Error(`String "${maybeConfigKey}" is not a valid config key.`);
}
Run Code Online (Sandbox Code Playgroud)

请注意,这可以保证字符串ConfigKey在运行时编译时都是有效的。


Pio*_*ski 8

最大的问题是如何处理所有可能的值,而不是在ConfigurationKeys没有明确检查每个值的情况下。我将它们命名为配置,因为这是非常常见的场景。

你可以隐藏你自己的保护函数背后的逻辑,告诉编译器:我可以处理类型检查,相信我。它由value is ConfigurationKeys返回类型识别。

代码示例 实时

type ConfigurationKeys = "foo" | "bar";

function isConfiguration(value: string): value is ConfigurationKeys {
    const allowedKeys: string[] = ["foo", "bar"];
    
    return allowedKeys.indexOf(value) !== -1;
}

const key: string = "alien" // Rather: some random function

if (isConfiguration(key)) { 
    // key => ConfigurationKeys
} else { 
    // key => string
}
Run Code Online (Sandbox Code Playgroud)

我发现编写自己的保护函数是使用联合类型的非常干净的解决方案。有时仍然需要类型转换,但在这里您将转换和逻辑隐藏在一段代码中。

参考:

  • 类型保护很棒,但这是目前编写的一种危险模式。如果“ConfigurationKeys”发生变化,并且您忘记更新“allowedKeys”以匹配,那么您将拥有一个类型保护,该类型保护在编译时*和*运行时错误地验证字符串。根据“ConfigurationKeys”类型检查“allowedKeys”是一个简单的更改。 (5认同)
  • 您还可以使用具有不同类型定义的 `find`,它允许与基类型不同的值。请参阅:`allowedKeys.find(el =&gt; el === value) !== undefined` (2认同)