前沿
最近在做项目的的时候,遇到一个TS类型的问题,需要使用到TS中的extends
和infer
来解决,像TS中这些内容用过之后老是忘记,这次坐下加单总结,记录一下。问题是这样的。
我先定义一个class,这个类中有属性和方法,内容如下
import { Observable, observable } from "@legendapp/state";
export class TableProcessor {
tableId: Observable<string>;
tableData: Observable<IResponseQuery<IDataResponse>>;
/**
* 刷新数据
*/
refresh = () =>{
//
}
// ...其它操作
}
我们看到属性tableId
和tableData
是个Observable范型,我们实例化这个类后,经过一个useSelector方法之后,想要直获取到属性的对应类型。过程如下
const tableProcessor = new TableProcessor();
function Demo() {
const [tableId] = useDataTableSelector(tableProcessor, s=> [s.tableId, s.tableData, s.refresh])
return <div>{tableId}<div>
}
经过useSelecotr
处理后,返回的tabldId
属性类型为string
, tableData
的类型为IResponseQuery<IDataResponse>
.这里已经没有范型。
TS中extends和infer使用
想要实现上述内容,我们需要使用ts中的extends
和infer
来处理。
extends 条件类型使用
在ts中,extends
中的用途有好多种,如果在类或接口中,可以使用 extends 关键字来继承或拓展一个或多个父类或接口,使用如下
class Animal {
name: string;
constructor(name: string, age: number) {
this.name = name;
}
eat() {
console.log(this.name + " is eating.");
}
}
class Dog extends Animal {}
在泛型中,可以使用 extends 关键字来约束泛型参数的类型范围,
interface Customer<T extends Object> {}
表示泛型 T 必须是 object 类型的子类型。
除类上述两种使用方案,extends
还可以是条件类型。在条件类型中,可以使用 extends 关键字来判断一个类型是否是另一个类型的子类型。
// 简单的类型匹配
type IsNum<T> = T extends number ? number : string
type Num = IsNum<1> // number;
type Str = IsNum<'1'> // string;
// 嵌套类型匹配
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";
type T0 = TypeName<string>; // "string"
type T1 = TypeName<"a">; // "string"
type T2 = TypeName<true>; // "boolean"
type T3 = TypeName<() => void>; // "function"
type T4 = TypeName<string[]>; // "object"
infer 使用
在TS中infer使用是指TypeScript中的infer
关键字,它是用来声明类型推断变量的。使用infer
关键字可以方便地从一个类型中提取出一个新的类型,这样就可以在类型中使用这个新的类型了。
// 使用 infer 声明一个类型变量 U,用于解析 T 的类型
// ExtractSelf<T> 类型:如果 T 可以被赋值给 U,就返回 U,否则返回 T
type ExtractSelf<T> = T extends infer U ? U : T;
type T1 = ExtractSelf<string>; // string
type T2 = ExtractSelf<() => void>; // () => void
// ExtractArrayItemType<T> 类型:如果 T 是一个数组类型,就返回数组元素的类型 U,否则返回 T
type ExtractArrayItemType<T> = T extends infer U[] ? U : T;
type A1 = ExtractArrayItemType<Date[]>; // Date
type A2 = ExtractArrayItemType<{ a: string }>; // { a: string }
// ExtractReturnType<T> 类型:如果 T 是一个函数类型,就返回函数返回值的类型 U,否则返回 T
type ExtractReturnType<T> = T extends () => infer U ? U : T;
type R1 = ExtractReturnType<() => number>; // number
type R2 = ExtractReturnType<(a: number) => number[]>; // number[]
实现
通过对ts中的extends
和infer
的了解,想要实现我们的需求,
- 先能获取到这个类中的所有属性和方法。
type DataTableProcessorCalssProps = typeof DataTableProcessor.prototype
- 定义一个类型,范型约束为我们之前定义的class。实现内容如下
// 去除属性为 observable类型转换为具体的类型
export type WipeObservable<T extends any> = {
[P in keyof T]: T[P] extends Observable<infer U> ? U : T[P];
}
这个类型的用途是将一个对象的属性中,如果是 Observable 类型的,就转换为 Observable 的泛型参数类型,否则保持不变.
- 使用
// 定义 DataTableProcesor wipe 类型
export type DataTableProcessorWipe = WipeObservable<DataTableProcessorCalssProps>;
export function useDataTableSelector<R extends any>(selector: (s: DataTableProcessorWipe) => R) {
return generateSelector<R, DataTableProcessorWipe>(Context, selector);
}
结束语
熟练使用ts中的extends
和infer
能实现我们好多需求,后面我们分析下TS自带的一些类型,也是用到这些内容,如ReturnType
, Parameters
等。
如果你觉得该文章不错,不妨
1、点赞,让更多的人也能看到这篇内容
2、关注我,让我们成为长期关系
3、关注公众号「前端有话说」,里面已有多篇原创文章,和开发工具,欢迎各位的关注,第一时间阅读我的文章
原文链接:https://juejin.cn/post/7241197449376825401 作者:杰出D