如何使用集合或数组在TypeScript中收窄类型?
问题描述
我有一个通用的string类型变量,想要将其收窄为泛型传入的字符串类型T(T extends string)。尝试用T[]或Set<T>做类型收窄没成功,因为string类型无法直接赋值给T——但这正是我想实现的目标。示例代码如下:
function inArray<T extends string>(arr: T[], input: string) { const optionSet = new Set(arr) // Set<T> const [foo] = input.split(','); // foo: string const inArray = arr.includes(foo); // 错误:类型'string'的参数不能赋值给类型'T'的参数。 const inSet = optionSet.has(foo); // 错误:类型'string'的参数不能赋值给类型'T'的参数。 }
我知道TypeScript无法静态确保foo是T类型,但集合/数组的检查正是我用来获取这种运行时保证的手段。有没有办法把变量设置为T类型?
解决方法
1. 类型断言(快速直接)
既然你确认运行时检查能保证foo属于T,可以直接用类型断言明确告诉TypeScript:
function inArray<T extends string>(arr: T[], input: string) { const optionSet = new Set(arr) const [foo] = input.split(','); const inArray = arr.includes(foo as T); const inSet = optionSet.has(foo as T); }
注意:类型断言是你在向TypeScript承诺foo确实符合T类型,要是运行时foo不在集合里,这种断言会引发潜在问题,务必保证你的检查逻辑可靠。
2. 自定义类型守卫(更严谨)
如果想让类型系统和运行时检查绑定得更紧密,可以写一个自定义类型守卫,让TypeScript明确知道当检查通过时foo就是T类型:
function isInSet<T extends string>(value: string, set: Set<T>): value is T { return set.has(value as T); } function inArray<T extends string>(arr: T[], input: string) { const optionSet = new Set(arr) const [foo] = input.split(','); if (isInSet(foo, optionSet)) { // 此处foo的类型已被自动收窄为T const inArray = arr.includes(foo); // 无错误 const inSet = optionSet.has(foo); // 无错误 } }
这种方式更安全,只有当类型守卫返回true时,TypeScript才会认定foo是T类型,避免了无条件断言的风险。
3. 利用数组find方法间接收窄
如果需要同时获取匹配值并确认类型,可以用数组的find方法:
function inArray<T extends string>(arr: T[], input: string) { const [foo] = input.split(','); const matched = arr.find(item => item === foo); if (matched) { // matched的类型自动收窄为T const inArray = arr.includes(matched); const inSet = new Set(arr).has(matched); } }
find会返回匹配的T类型值(或undefined),通过if (matched)的判断,TypeScript会自动将matched的类型收窄为T。
内容的提问来源于stack exchange,提问作者Khaled
相关产品推荐
相关产品推荐

