Extract 概述
- 在TypeScript这一逐渐成为前端开发首选的静态类型检查语言中,类型系统提供了丰富的工具来帮助开发者编写更加健壮和可维护的代码。
- 其中,
Extract<T, U>是一个强大的内置实用类型,用于从一个联合类型T中提取出属于另一个类型U的那些类型。 - 本文将深入探讨Extract类型的工作原理、应用场景以及如何有效利用它来提升代码的类型安全性和表达力。
什么是Extract<T, U>?
- Extract<T, U>是一个条件类型,其定义为:从类型T中选取那些同时也可以被类型U赋值的类型
- 简而言之,它像一个过滤器,保留了T中与U兼容的部分
- 在TypeScript的类型系统中,兼容性意味着一个类型可以被另一个类型所赋值
- 例如,任何派生自基类的类型都可以赋值给基类类型
基本用法
type A = string | number | boolean;
type B = number | boolean;
type C = Extract<A, B>; // 结果为: number | boolean
- 在这个例子中,Extract<A, B>的结果是A类型中与B兼容的类型,即number和boolean
- 但是如果是下面这样,则情况不一样
type A = string | number; type B = boolean; type C = Extract<A, B>; // 结果为: never- 这里就是 never
应用场景
-
类型筛选:在处理复杂的联合类型时,Extract可以帮助我们精炼出特定的类型,特别是在大型项目中处理API响应或状态管理时,能够有效地缩小类型范围,提高代码的可读性和准确性
-
泛型约束:结合泛型使用,Extract可以创建更加灵活且精确的类型约束。例如,在定义一个函数时,可以约束参数类型必须是某联合类型的一个子集,从而限制函数的适用范围
-
类型映射:在实现类型映射或类型转换时,Extract可以帮助我们基于类型兼容性进行有选择的映射,特别是在处理多态或复杂类型系统设计时
实战案例
1 )从API响应中提取特定类型
- 假设你正在处理一个API响应,该响应可能包含多种类型的数据,但你只关心其中的某些类型
type ApiResponse = { type: 'user'; data: User } | { type: 'post'; data: Post } | { type: 'comment'; data: Comment }; type UserSpecificResponse = Extract<ApiResponse, { type: 'user' }>;
2 ) 泛型约束与类型映射
- 考虑一个函数,它应该只处理某些特定类型的对象集合
// UserSpecificResponse 现在是 { type: 'user'; data: User } type Animal = { kind: 'dog' | 'cat' | 'bird' }; type Canine = { kind: 'dog' }; function processCaninesOnly<T>(items: Array<Extract<T, Canine>>): void { // ... } const dogs: Array<{ kind: 'dog' }> = [{ kind: 'dog' }, { kind: 'dog' }]; processCaninesOnly(dogs); // 正确,因为dogs数组元素的kind只能是'dog'
3 ) 高级应用
3.1 基于 Partial 对两个类型求交集
// 定义两个接口
interface Button {
btntype: string;
text: string;
}
interface Link {
href: string;
text: string; // 假设 Link 接口也有一个 text 属性,以便演示共有属性的合并
}
// 定义一个函数,接受两个对象,并返回一个包含两者共有属性的新对象
function mergeCommonProperties<T extends object, U extends object>(obj1: T, obj2: U): Partial<T> & Partial<U> {
const result: Partial<T> & Partial<U> = {};
// 使用 for...in 循环遍历 obj1 的所有属性
for (const key in obj1) {
if (obj1.hasOwnProperty(key) && (key as string) in obj2) {
// 如果 key 在 obj2 中也存在,则将其添加到结果对象中
// 无需显式类型断言,因为 TypeScript 可以自行推断类型
result[key] = obj1[key];
}
}
return result;
}
// 创建两个符合接口的对象
const button: Button = {
btntype: 'normal',
text: 'Click Me',
};
const link: Link = {
href: 'http://example.com',
text: 'Link Text',
};
// 使用函数尝试合并共有属性
const merged = mergeCommonProperties(button, link);
console.log(merged); // 输出:{ text: 'Click Me' },因为 text 是 Button 和 Link 的共有属性
3.2 基于 in 和 Extract 优化求类型交集
// 定义两个接口
interface Button {
btntype: string;
text: string;
}
interface Link {
href: string;
text: string; // 假设 Link 接口也有一个 text 属性,以便演示共有属性的合并
}
// 使用 Extract 和 keyof 来提取两个类型中共有的属性键
type CommonProperties<T, U> = {
[P in Extract<keyof T, keyof U>]: T[P];
};
// 定义一个函数,接受两个对象,并返回一个包含两者共有属性的新对象
function mergeCommonProperties<T extends object, U extends object>(obj1: T, obj2: U): CommonProperties<T, U> {
const result: Partial<CommonProperties<T, U>> = {};
// 遍历 obj1 的所有属性键
(Object.keys(obj1) as Array<keyof T>).forEach(key => {
// 检查该属性是否也存在于 obj2 中
if ((key as string) in obj2) {
// 断言 key 为 Extract<keyof T, keyof U> 类型,表示它是 T 和 U 的共有属性
const commonKey = key as Extract<keyof T, keyof U>;
// 将共有的属性添加到结果对象中
result[commonKey] = obj1[commonKey];
}
});
// 返回结果,通过类型断言确保类型匹配
return result as CommonProperties<T, U>;
}
// 创建两个符合接口的对象
const button: Button = {
btntype: 'normal',
text: 'Click Me',
};
const link: Link = {
href: 'http://example.com',
text: 'Link Text',
};
// 使用函数尝试合并共有属性
const merged = mergeCommonProperties(button, link);
console.log(merged); // 输出:{ text: 'Click Me' },因为 text 是 Button 和 Link 的共有属性
Extract<T, U>类型是TypeScript中一个强大的工具,它通过条件类型实现了类型筛选,增强了类型系统的灵活性和表达力- 在处理复杂类型逻辑、泛型约束以及类型映射时,Extract能够帮助开发者更精细地控制类型系统,写出更加健壮和易于维护的代码
- 通过上述介绍和案例,希望你能够掌握并充分利用Extract类型在实际项目中的应用,进一步提升你的TypeScript编码体验
380



被折叠的 条评论
为什么被折叠?



