我有一个给定联合类型的数组,然后想检查一个来自联合类型超集的字符串是否包含在数组中(运行时检查):
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
修复1可以,但不是很优雅。修复2愚弄编译器,但是误导和低效的运行时。在我的例子中,“外来”字符串类型只是不在联合类型中的任何字符串的占位符。
有没有什么方法可以在没有强制转换或显式测试的情况下编译?可以否定表达式,以便我们让这个“类型后卫”起作用吗?
顺便说一句:这个非常酷的答案显示了如何从值列表中构造一个类型化的元组:Typescript derive union type from tuple/array values
答案 0 :(得分:3)
最大的问题是如何在没有明确检查每个值的情况下处理非ConfigurationKeys
的所有可能值。我将它们命名为 Configuration ,因为它是非常常见的场景。
你可以隐藏你自己的守护函数背后的逻辑,告诉编译器:我可以处理类型检查,相信我。它被value is ConfigurationKeys
返回类型识别。
代码示例 (live):
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
}
我发现编写自己的防护功能是非常干净的解决方案,可以使用Union类型。有时仍然需要类型转换,但是在这里你可以在单个代码段中隐藏转换和逻辑。
参考:
答案 1 :(得分:3)
被接受的答案使用类型断言/广播,但从注释来看,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.`);
}
请注意,这可以保证在运行时和编译时,字符串都是有效的ConfigKey
。
答案 2 :(得分:-2)
我找到了一个可能更安全的解决方案:
const knownStrings = {
abc: true,
def: true,
};
const isKnownString = (name: string): name is keyof typeof knownStrings => name in knownStrings;
这不需要任何类型转换,唯一的缺点是冗余的true
值。