在上一篇我重點講述了 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