TypeScript中的泛型工具

news/2024/5/20 3:36:48 标签: ts

原文链接:https://juejin.cn/post/6844904147167215624#heading-1 

本期涉及的操作符如下:

  • Partial
  • Required
  • Readonly
  • Pick<T,K extends keyof T>
  • Record<K extends keyof any, T>
  • Exclude<T,U>
  • Extract<T,U>
  • Omit<T, K extends keyof any>

首先还是先讲述一下ts中的这些高级操作符,如果都已经掌握了,可以直接跳到末尾的手撕笔试题。手撕笔试题

Partial

Partial 将属性变为可选属性。举个栗子,iUser 这个接口 name 和 age 是必须的,但是同时又有另一个接口 iOptionUser,接口属性完全一样,只是里面的 name 和 age 是可选的。比较笨的方法当然是手动再写一个。

interface iUser {
  name: string;
  age: number;
}
interface iOptionUser {
  name?: string;
  age?: number;
}

其实,我们可以看到的是,iOptionUser 只是在属性后添加一个?接口。我们可以简单实现如下(该方法已内置)

type Partial<T> = {
  [P in keyof T]?: T[P];
};

 

Required

Required和Partial方法正好相反,是将属性变成必须。方法同样非常简单,可以这样实现(该方法已内置)

type Required<T> = {
    [P in keyof T]-?: T[P];
};

效果如下:

 

Readonly

Readonly是将属性变成只读。方法同样非常简单,可以这样实现(该方法已内置)

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

效果如下:

 

Pick<T,K extends keyof T>

Pick顾名思义,就是把一些属性挑选出来。效果如下:

 

大家可以思考一下怎么实现,官方源码如下:

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};
复制代码

Record<K extends keyof any, T>

Record用于创建一个具有同类型属性值的对象。

type Record<K extends keyof any, T> = {
    [P in K]: T;
};

Exclude<T,U>

从类型 T 中剔除所有可以赋值给 U 的属性,然后构造一个类型。主要用于联合类型。

 

官方源码如下:

type Exclude<T, U> = T extends U ? never : T;

Extract<T,U>

功能与 Exclude相反

 

type Extract<T, U> = T extends U ? T : never;

Omit<T, K extends keyof any>

主要用于剔除interface中的部分属性。 比如接口iUser包含name、age、firstName、lastName、location属性,而接口iUser2不包含location属性,我们可以使用前面提到的Pick实现,但这样会比较复杂,所以有了Omit 操作符。

interface iUser {
    name: string;
    age: number;
    firstName: string;
    lastName: string;
    location: string;
}
interface iUser2 {
    name: string;
    age: number;
    firstName: string;
    lastName: string;
}

效果如下:

 

Omit源码如下:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

手撕笔试题

这是一道 leetcode 的 ts笔试题,原题目略长,就不直接贴出来了,这里简化一下:

// 假设有一个这样的类型:
interface initInterface {
  count: number;
  message: string;
  asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>;
  syncMethod<T, U>(action: Action<T>): Action<U>;
}
// 在经过 Connect 函数之后,返回值类型为

type Result {
  asyncMethod<T, U>(input: T): Action<U>;
  syncMethod<T, U>(action: T): Action<U>;
}
// 其中 Action<T> 的定义为:
interface Action<T> {
  payload?: T
  type: string
}
// 现在要求写出Connect的函数类型定义。

首先我们需要明白这个题义,这里是需要我们把initInterface里的非函数属性去除,并且函数签名发生了变化。

  1. 第一步:获取函数属性
type RemoveNonFunctionProps<T> = {
    [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];

type FunctionProps = RemoveNonFunctionProps<initInterface>;

 

2. 将只包含函数属性的类型Pick出来

type PickFunction<T> = Pick<T, RemoveNonFunctionProps<T>>;
type iFunctionInterface = PickFunction<initInterface>;

 

3.接下来就是函数转换的过程,这里需要用到我上篇博文提到的infer。

我们对比一下,转换前后的函数签名,发现只是去除了参数和返回结果的Promsie。

type asyncMethod<T, U> = (input: Promise<T>) => Promise<Action<U>>;
type transformAsyncMethod<T,U> = (input: T) => Action<U>;

我们使用infer可以这样做

type TransformASyncMethod<T> = T extends (
  input: Promise<infer U>
) => Promise<Action<infer S>>
  ? (input: U) => Action<S>
  : never;

 

同理我们看一下方法二,转换前后:

type syncMethod<T, U> = (action: Action<T>) => Action<U>;
type transformSyncMethod<T, U> = (action: T) => Action<U>;

我们依旧使用infer

type TransformSyncMethod<T> = T extends (
  action: Action<infer U>
) => Action<infer S>
  ? (action: U) => Action<S>
  : never;

 

所以转换函数可以这样写:

type TransformMethod<T> = T extends (
  input: Promise<infer U>
) => Promise<Action<infer S>>
  ? (input: U) => Action<S>
  : T extends (action: Action<infer U>) => Action<infer S>
  ? (action: U) => Action<S>
  : never;

4.整合 前三步,我们已经有了完整的思路,现在就是把Connect类型定义整合起来。

type RemoveNonFunctionProps<T> = {
  [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
type PickFunction<T> = Pick<T, RemoveNonFunctionProps<T>>;
type TransformMethod<T> = T extends (
  input: Promise<infer U>
) => Promise<Action<infer S>>
  ? (input: U) => Action<S>
  : T extends (action: Action<infer U>) => Action<infer S>
  ? (action: U) => Action<S>
  : never;
type ConnectAll<T> = {
  [K in keyof T]: TransformMethod<T[K]>;
};
type Connect<T> = ConnectAll<PickFunction<T>>;


http://www.niftyadmin.cn/n/951494.html

相关文章

俞敏洪+马云+牛根生+史玉柱经典语录

&#xff11;&#xff0e;女人如果因为觉得一个男生帅就跟嫁给他&#xff0c;这是好色&#xff1b;男生因为女生漂亮而娶她&#xff0c;是审美。&#xff12;.为什么你不要自傲和自卑&#xff1f;你可以说自己是最好的&#xff0c;但不能说自己是全校最好的、全北京最好的、全国…

Cesium orientation 和 设置初始角度

一. Cesium orientation orientation-相机镜头对准的方法.heading-代表镜头左右方向,正值为右,负值为左,360度和0度是一样的 pitch-代表镜头上下方向,正值为上,负值为下. roll-代表镜头左右倾斜.正值,向右倾斜,负值向左倾斜 二. 设置初始化角度 1.先用鼠标手动调整出你想要的角…

假如生活欺骗了你

最近某好友的公司&#xff0c;到了一年一度的调职调薪时段。 作为一个独立的中立的第三方&#xff0c;我听该好友详细讲述了他们公司的一些情况&#xff0c;颇有感触。 调职调薪的结果&#xff0c;比较杯具。 就公司内部的 职称/头衔/Title而言&#xff0c;大多数人都有提升1级…

Cesium 中的pick

在cesium中&#xff0c;想获取不同的对象&#xff0c;需要通过pick方法来进行拾取&#xff0c;但是Cesium中有多种pick的方法&#xff0c;例如 scene中有pick、pickPosition、及drillPick等&#xff0c;camera中有getPickRay、pickEllipsoid等&#xff0c;globel中有pick&#…

npm ERR

npm ERR! node-sass4.14.1 postinstall: node scripts/build.js 网速不好或者版本过高&#xff0c;用淘宝镜像安装 npm config set sass_binary_sitehttps://npm.taobao.org/mirrors/node-sass

常用坐标系统

一对名词&#xff1a;WKID与EPSG WKID即Well Known ID&#xff0c;众所周知的ID号的意思。EPSG是管理这些ID号的一个组织&#xff0c;网站是 EPSG.io: Coordinate Systems Worldwide 如&#xff1a;查看4490 如&#xff1a;查看4490 https://epsg.io/China Geodetic Coor…

“赢在中国”对80后的30个忠告

1.一个年轻人&#xff0c;如果三年的时间里&#xff0c;没有任何想法&#xff0c;他这一生&#xff0c;就基本这个样子&#xff0c;没有多大改变了。 2.成功者就是胆识加魄力&#xff0c;曾经在火车上听人谈起过温州人的成功&#xff0c;说了这么三个字&#xff0c;胆子大。这其…

http://localhost:8080/undefined/Assets/IAU2006_XYS/IAU2006_XYS_17.json

记vue 加载cesium 框架时&#xff0c;加载静态文件&#xff0c;路径中出现undefined 解决方法&#xff1a; 在index.html中引入一下就可以了