infer
是在typescript 2.8
中新增的關鍵字,距離如今3.9.3
已經有兩年出頭了,趁着今天恰好使用了infer
,因此好好整理一番javascript
infer
能夠在extends
的條件語句中推斷待推斷的類型html
例如在文檔的示例中,使用infer
來推斷函數的返回值類型java
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type func = () => number;
type variable = string;
type funcReturnType = ReturnType<func>; // funcReturnType 類型爲 number
type varReturnType = ReturnType<variable>; // varReturnType 類型爲 string
複製代碼
在這個例子中,infer R
表明待推斷的返回值類型,若是T
是一個函數,則返回函數的返回值,不然返回any
git
僅僅經過這一個例子,是很難看出infer
是用來幹什麼的,還須要多看幾個例子github
infer
的做用不止是推斷返回值,還能夠解包,我以爲這是比較經常使用的typescript
假如想在獲取數組裏的元素類型,在不會infer
以前我是這樣作的數組
type Ids = number[];
type Names = string[];
type Unpacked<T> = T extends Names ? string : T extends Ids ? number : T;
type idType = Unpacked<Ids>; // idType 類型爲 number
type nameType = Unpacked<Names>; // nameType 類型爲string
複製代碼
上次我寫了20多行,就爲了獲取一堆各類不一樣類型的數組裏的元素類型,然而若是使用infer
來解包,會變得十分簡單函數
type Unpacked<T> = T extends (infer R)[] ? R : T;
type idType = Unpacked<Ids>; // idType 類型爲 number
type nameType = Unpacked<Names>; // nameType 類型爲string
複製代碼
這裏T extends (infer R)[] ? R : T
的意思是,若是T
是某個待推斷類型的數組,則返回推斷的類型,不然返回T
post
再好比,想要獲取一個Promise<xxx>
類型中的xxx
類型,在不使用infer
的狀況下我想不到何解學習
type Response = Promise<number[]>;
type Unpacked<T> = T extends Promise<infer R> ? R : T;
type resType = Unpacked<Response>; // resType 類型爲number[]
複製代碼
仍是官方文檔的例子
type Foo<T> = T extends { a: infer U; b: infer U } ? U : never;
type T10 = Foo<{ a: string; b: string }>; // T10類型爲 string
type T11 = Foo<{ a: string; b: number }>; // T11類型爲 string | number
複製代碼
同一個類型變量在推斷的值有多種狀況的時候會推斷爲聯合類型,針對這個特性,很方便的能夠將元組轉爲聯合類型
type ElementOf<T> = T extends (infer R)[] ? R : never;
type TTuple = [string, number];
type Union = ElementOf<TTuple>; // Union 類型爲 string | number
複製代碼
在React
的typescript
源碼中應該經常使用infer
就拿useReducer
來舉例子,若是咱們這樣使用useReducer
const reducer = (x: number) => x + 1;
const [state, dispatch] = useReducer(reducer, '');
// Argument of type "" is not assignable to parameter of type 'number'.
複製代碼
這裏useReducer
會報一個類型錯誤,說""
不能賦值給number
類型
那麼React
這裏是如何經過reducer
函數的類型來判斷state
的類型呢?
查看userReducer
的定義,定義以下
function useReducer<R extends Reducer<any, any>, I>(
reducer: R,
// ReducerState 推斷類型
initializerArg: I & ReducerState<R>,
initializer: (arg: I & ReducerState<R>) => ReducerState<R>
): [ReducerState<R>, Dispatch<ReducerAction<R>>];
// infer推斷
type ReducerState<R extends Reducer<any, any>> = R extends Reducer<infer S, any>
? S
: never;
// Reducer類型
type Reducer<S, A> = (prevState: S, action: A) => S;
複製代碼
一切明瞭了,使用了infer
推斷reducer
函數裏的state
參數類型
今天使用ant-design-chart
,庫裏沒有把Ref
的定義導出,因此只能本身取了
// 已知
type ref = React.MutableRefObject<G2plotStackedBar | undefined>;
// 求 ???
const chartRef = useRef<???>()
複製代碼
有了上面的學習,這裏就很簡單了,只須要取出React.MutableRefObject
裏的內容,一行infer
搞定
// infer推斷
type ChartRef<T> = T extends React.MutableRefObject<infer P> ? P : never;
const chartRef = useRef<ChartRef<ref>>()
複製代碼
infer
是很是有用的,若是想要擺脫僅僅是在寫帶類型的javascript
,高級特性必定要了解
我可能一年前就看見infer
了,一直沒有好好學,緣由除了本身懶,還有就是水平確實不夠,今年再學明顯感受不一樣了。
再推薦一篇很好的文章,我也是看了這篇文章纔好好學習了一下infer
,這篇文章講的更復雜一點
Vue3 跟着尤雨溪學 TypeScript 之 Ref 類型從零實現
原題就不貼出了,在這裏能夠看見 github
分享一下個人思路
extends
關鍵字能夠判斷是不是函數,是返回鍵名,不是返回never
,最後使用映射類型[keyof T]
的方式來獲取鍵名的聯合類型,由於never
和任何類型組聯合類型都會過濾掉never
,因此天然排除了never
infer
硬推題解以下:
type EffectModuleFuncName = {
[K in keyof EffectModule]: EffectModule[K] extends Function ? K : never;
}[keyof EffectModule];
type UnPackedPromise<T> = T extends Promise<infer P> ? P : T;
type EffectModuleFunc<T> = T extends (params: infer P) => infer U
? P extends Promise<infer R>
? (v: R) => UnPackedPromise<U>
: P extends Action<infer X>
? (v: X) => UnPackedPromise<U>
: never
: never;
// 修改 Connect 的類型,讓 connected 的類型變成預期的類型
type Connect = (
module: EffectModule,
) => { [K in EffectModuleFuncName]: EffectModuleFunc<EffectModule[K]> };
複製代碼
也不知道本身寫的對不對,總以爲怪怪的,能夠討論一下
參考資料:
最後,祝你們身體健康,工做順利!
歡迎你們關注個人公衆號~