在上一篇我重点讲述了 ts 的 keyof、in 以及 infer。本期将结合一道笔试题重点讲述 ts 的一些其余内置操做符 。git
本期涉及的操做符以下:github
首先仍是先讲述一下ts中的这些高级操做符,若是都已经掌握了,能够直接跳到末尾的手撕笔试题。手撕笔试题typescript
Partial 将属性变为可选属性。举个栗子,iUser 这个接口 name 和 age 是必须的,可是同时又有另外一个接口 iOptionUser,接口属性彻底同样,只是里面的 name 和 age 是可选的。比较笨的方法固然是手动再写一个。bash
interface iUser { name: string; age: number; } interface iOptionUser { name?: string; age?: number; } 复制代码
其实,咱们能够看到的是,iOptionUser 只是在属性后添加一个?接口。咱们能够简单实现以下(该方法已内置)微信
type Partial<T> = { [P in keyof T]?: T[P]; }; 复制代码
Required和Partial方法正好相反,是将属性变成必须。方法一样很是简单,能够这样实现(该方法已内置)markdown
type Required<T> = { [P in keyof T]-?: T[P]; }; 复制代码
效果以下:async
Readonly是将属性变成只读。方法一样很是简单,能够这样实现(该方法已内置)函数
type Readonly<T> = { readonly [P in keyof T]: T[P]; }; 复制代码
效果以下:oop
Pick顾名思义,就是把一些属性挑选出来。效果以下:post
你们能够思考一下怎么实现,官方源码以下:
type Pick<T, K extends keyof T> = { [P in K]: T[P]; }; 复制代码
Record用于建立一个具备同类型属性值的对象。
type Record<K extends keyof any, T> = { [P in K]: T; }; 复制代码
从类型 T 中剔除全部能够赋值给 U 的属性,而后构造一个类型。主要用于联合类型。
官方源码以下:
type Exclude<T, U> = T extends U ? never : T; 复制代码
功能与 Exclude相反
type Extract<T, U> = T extends U ? T : never; 复制代码
主要用于剔除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里的非函数属性去除,而且函数签名发生了变化。
type RemoveNonFunctionProps<T> = { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]; type FunctionProps = RemoveNonFunctionProps<initInterface>; 复制代码
type PickFunction<T> = Pick<T, RemoveNonFunctionProps<T>>; type iFunctionInterface = PickFunction<initInterface>; 复制代码
咱们对比一下,转换先后的函数签名,发现只是去除了参数和返回结果的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>>; 复制代码
本次就到这里了,下面是我我的的微信公众号。
@Author: WaterMan