TypeScript泛型函数如何从入参自动推断类型约束数组成员
解决方案
原实现的类型漏洞来自TypeScript泛型的默认推导规则:泛型T会同时从values数组、value两个入参位置收集候选类型,最终取联合类型作为T的实际类型。因此当你向数组传入任意字符串时,该字符串会被自动加入T的联合类型范围,自然不会触发类型报错。
要实现无需手动传泛型、自动从value参数推导类型并校验数组元素,只需要阻止TS从第一个values参数位置推导泛型T,让T的类型完全由第二个value参数决定即可。
TS 5.4+ 直接使用内置工具类型
TS 5.4 正式内置了NoInfer工具类型,作用就是标记指定类型位置不参与泛型推导,仅做类型校验,直接修改函数定义即可生效:
export function isValueInArray<T extends string>( values: NoInfer<T>[], value: T | undefined ) { return value ? values.includes(value) : false; }
TS 5.4 以下版本兼容写法
如果使用的TS版本低于5.4,可以手动实现等价的NoInfer类型,配合原函数使用即可:
// 低版本等价NoInfer实现 type NoInfer<T> = [T][T extends any ? 0 : never]; export function isValueInArray<T extends string>( values: NoInfer<T>[], value: T | undefined ) { return value ? values.includes(value) : false; }
实际效果
修改后所有场景都会自动完成类型推导和校验,不需要手动传入泛型参数:
- 输入数组元素时,会自动根据
value的类型给出合法值补全提示 - 传入不在
value类型范围内的数组元素时,TS会直接抛出类型错误
export type Status = 'OPEN' | 'CANCELED' | 'ACCEPTED' | 'INVALIDATED'; declare const buyNowStatus: Status; // 合法传参,无报错 const isValid = isValueInArray(['OPEN'], buyNowStatus); // 非法传参,TS报错:类型'"ANY"'的参数不能赋给类型'Status'的参数 const shouldBeInvalid = isValueInArray(['OPEN', 'ANY'], buyNowStatus); function checkStatus(status: Status) { // 非法传参,TS报错:类型'"ANY"'的参数不能赋给类型'Status'的参数 return isValueInArray(['ANY'], status); }
边界情况说明
当传入的value为undefined时,因为没有可用于推导T的类型来源,T会回退到string约束,数组可以传入任意字符串。这个行为和原函数逻辑完全一致:value为undefined时函数固定返回false,不需要校验数组元素类型,不会产生逻辑问题。
内容的提问来源于stack exchange,提问作者gosseti
相关产品推荐
相关产品推荐

