借助TypeScript 2.8的Exclude类型优化partition函数类型
优化TypeScript中partition函数的类型定义(利用TS 2.8条件类型)
嘿,这个需求太赞了!TypeScript 2.8引入的条件类型(尤其是Exclude)确实能让partition函数的类型推断变得更精准,不再让返回的两个数组都只能是笼统的T[]。咱们一步步来实现这个优化:
问题分析
原来的partition类型定义:
function partition<T>(list: T[], predicate:(x:T)=>boolean): [T[],T[]];
不管谓词逻辑如何,返回的两个数组都只能被推断为T[]。比如如果传入一个number | string的联合类型数组,你没办法直接从返回值里得到“纯数字数组”和“纯字符串数组”的类型,只能靠后续类型断言,不够优雅。
优化方案:结合类型谓词与条件类型
我们可以通过函数重载+条件类型来实现精准的类型划分:
// 定义类型谓词的类型,约束能判断元素子类型的谓词 type TypeGuardPredicate<T, U extends T> = (x: T) => x is U; // 第一个重载:针对使用类型谓词的场景,返回精准划分后的数组类型 function partition<T, U extends T>( list: T[], predicate: TypeGuardPredicate<T, U> ): [U[], Exclude<T, U>[]]; // 第二个重载:兼容原有普通boolean返回值的谓词调用,保持向后兼容 function partition<T>( list: T[], predicate: (x: T) => boolean ): [T[], T[]]; // 函数实现(类型宽松处理,重载已约束好类型规则) function partition<T>(list: T[], predicate: (x: T) => any): [T[], T[]] { const pass: T[] = []; const fail: T[] = []; for (const item of list) { predicate(item) ? pass.push(item) : fail.push(item); } return [pass, fail]; }
效果演示
1. 联合类型数组的精准划分
这是最能体现优势的场景:
type MixedArrayItem = number | string | boolean; const mixedList: MixedArrayItem[] = [1, "hello", true, 42, "world"]; // 使用类型谓词判断元素是否为number const [numbers, nonNumbers] = partition(mixedList, (x): x is number => typeof x === "number"); // numbers 的类型是 number[] // nonNumbers 的类型是 Exclude<MixedArrayItem, number>[] → (string | boolean)[]
2. 原有普通谓词调用的兼容性
如果你还是用普通的布尔返回值谓词,类型系统会自动回退到原有行为,完全兼容:
const [evens, odds] = partition([1,2,3,4], x => x % 2 === 0); // evens 和 odds 的类型都是 number[],和原来的行为一致
原理说明
Exclude<T, U>是TS 2.8新增的条件类型,本质是T extends U ? never : T,用来从T中排除所有可赋值给U的类型。- 第一个重载利用类型谓词(
x is U)让TypeScript能推断出哪些元素符合条件,再通过Exclude<T, U>得到不符合条件的元素类型,从而让返回值的类型更精准。 - 重载的设计保证了原有代码无需修改就能正常工作,同时给需要精准类型的场景提供了支持。
内容的提问来源于stack exchange,提问作者Emmanuel Touzery
相关产品推荐
相关产品推荐

