学会TS中extends和infer,解决实际问题,快来看看吧

前沿

最近在做项目的的时候,遇到一个TS类型的问题,需要使用到TS中的extendsinfer来解决,像TS中这些内容用过之后老是忘记,这次坐下加单总结,记录一下。问题是这样的。

我先定义一个class,这个类中有属性和方法,内容如下

import { Observable, observable } from "@legendapp/state";
export class TableProcessor {
  tableId: Observable<string>;
  
  tableData: Observable<IResponseQuery<IDataResponse>>;

  /**
   * 刷新数据
   */
  refresh = () =>{
    //
  }
  // ...其它操作
}

我们看到属性tableIdtableData 是个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中的extendsinfer来处理。

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中的extendsinfer的了解,想要实现我们的需求,

  1. 先能获取到这个类中的所有属性和方法。
 type DataTableProcessorCalssProps = typeof DataTableProcessor.prototype
  1. 定义一个类型,范型约束为我们之前定义的class。实现内容如下
// 去除属性为 observable类型转换为具体的类型 
export type WipeObservable<T extends any> = {
  [P in keyof T]: T[P] extends Observable<infer U> ? U : T[P];
}

这个类型的用途是将一个对象的属性中,如果是 Observable 类型的,就转换为 Observable 的泛型参数类型,否则保持不变.

  1. 使用
// 定义 DataTableProcesor wipe 类型
export type DataTableProcessorWipe = WipeObservable<DataTableProcessorCalssProps>;


export function useDataTableSelector<R extends any>(selector: (s: DataTableProcessorWipe) => R) {
  return generateSelector<R, DataTableProcessorWipe>(Context, selector);
}

结束语

熟练使用ts中的extendsinfer能实现我们好多需求,后面我们分析下TS自带的一些类型,也是用到这些内容,如ReturnType, Parameters 等。

如果你觉得该文章不错,不妨

1、点赞,让更多的人也能看到这篇内容

2、关注我,让我们成为长期关系

3、关注公众号「前端有话说」,里面已有多篇原创文章,和开发工具,欢迎各位的关注,第一时间阅读我的文章

原文链接:https://juejin.cn/post/7241197449376825401 作者:杰出D

(0)
上一篇 2023年6月6日 上午10:21
下一篇 2023年6月7日 上午10:00

相关推荐

发表回复

登录后才能评论