TypeScript如何实现带元素类型约束的数组自定义类型守卫
解决方案
核心实现
首先定义工具类型提取入参中所有可能的数组元素类型,再优化类型守卫的泛型约束即可满足你的需求,完整实现如下:
// 工具类型:提取联合类型中所有数组的元素类型 type ArrayElementOfUnion<T> = T extends Array<infer E> ? E : never; // 优化后的数组类型守卫 const isArray = <T extends ArrayElementOfUnion<U> = ArrayElementOfUnion<U>, U>(value: U): value is T[] => { // 运行时校验请使用原生Array.isArray,避免类数组对象被误判 return Array.isArray(value); };
问题解答
- 不需要手动传入第二个泛型:泛型
U会自动从你传入的实参推导类型,完全不需要显式声明。 - 自动约束泛型为数组元素类型:通过
ArrayElementOfUnion工具类型,会自动提取入参所有可能的数组元素类型作为泛型T的约束,当入参类型为number[] | string[]时,T允许传入的类型只能是string | number或它们的子集。
用例验证
场景1:指定元素类型校验
const func = function(arg: string | string[] | number | number[]) { // 合法:string属于允许的元素类型集合,守卫会将arg收窄为string[] if (isArray<string>(arg)) { console.log(arg) } // 非法:传入boolean会直接报类型错误,符合你的预期 // isArray<boolean>(arg) }
场景2:不指定元素类型,仅校验是否为数组
const func = function(arg: string | string[] | number | number[]) { if (isArray(arg)) { // arg被收窄为string[] | number[] console.log(arg) } else { // arg被收窄为string | number console.log(arg) } }
场景3:入参仅为数组联合类型的情况
const func = function(arg: string[] | number[]) { // 合法,不会报错 if (isArray<string>(arg)) { // arg被收窄为string[] console.log(arg) } else { // arg被收窄为number[] console.log(arg) } }
内容的提问来源于stack exchange,提问作者Ernest Jones
相关产品推荐
相关产品推荐

