TS 內置的Promise.all
,在lib.es2015.promise.d.ts
文件中聲明,經過函數重載定義多個泛型進行類型聲明的。ajax
而在最新的 TS(4.1.3) 中已經有比較優雅的方法進行聲明瞭,所以這篇文章的做用就是介紹怎麼寫出比較優雅一個Promise.all
類型。(不包括函數實現)數組
在某個版本之前,聲明元組只能經過[string, typeof X, number]
一個個手動聲明,而如今能夠經過as const
進行聲明元組,用法以下:promise
const tuple = ['你好', '元組', 17] as const
// ^^^^^ = readonly ["你好", "元組", 17]
複製代碼
能夠看到這樣就聲明瞭一個元組,以前的話就得一個個寫元組元素聲明。markdown
假設依舊有上面的tuple
變量,如今有個需求須要把tuple
變量的每一個元素都轉成Promise<元素>
類型,而這時候就須要使用映射元組的技巧了,語法和映射類型一致。編輯器
type TuplePromise<T> = {
[K in keyof T]: Promise<T[K]>
}
type T1 = TuplePromise<typeof tuple>
// ^^ = readonly [Promise<"你好">, Promise<"元組">, Promise<17>]
複製代碼
假設以後都有以下六個類型函數
const ajax1: Promise<string> = Promise.resolve(':)')
const ajax2: Promise<number> = Promise.resolve(17)
const ajax3: Promise<boolean> = Promise.resolve(true)
const ajax4: string = ':)'
const ajax5: number = 17
const ajax6: boolean = true
const ajaxArr = [ajax1, ajax2, ajax3, ajax4, ajax5, ajax6] as const
複製代碼
在lib.es2015.promise.d.ts
文件中能夠找到對應的函數聲明,建議經過 VSC 編輯器中使用ctrl+鼠標左鍵Promise.all
跳轉定義,定義以下圖。測試
能夠看到源碼類型是經過使用泛型T
進行類型聲明的,源碼中最多參數只能有10個,由於定義的重載只有10個,最後一個就是T1-T10
,因此當參數超過十個的時候就會報錯。(雖然不會有這個場景)ui
Promise.all
行爲,由於使用的泛型,所以能夠不用傳入元組,傳入數組也能識別。spa
Promise.all([ajax1, ajax2, ajax3, ajax4, ajax5, ajax6])
// 這是運行時類型 Promise.all([Promise<string>, Promise<number>, Promise<boolean>, string, number, boolean])
// 返回 Promise<[string, number, boolean, string, number, boolean]>
.then(res => {})
// res: [string, number, boolean, string, number, boolean]
複製代碼
能夠看到是Promise
的話就會拆出裏面.then
參數的類型,若是不是則原樣返回。經過源碼,咱們能夠看出是用PromiseLike
的類型來進行拆解的,這是由於Promise.all
能夠使用含有.then
的對象。code
所以只要含有.then
方法,就要拆出方法參數的類型。
myPromiseAll
類型只能接受一個元組參數,而後經過元組映射進行拆解,最後返回Promise<元組映射結果>
。
由上一節能夠得出咱們須要一個類型來提取.then
的方法參數類型,這個很簡單,能夠使用內置的PromiseLike
類型判斷是否含有.then
方法且還會自動獲取方法參數類型,所以經過infer
能夠輕鬆取出來。
type GetPromiseLikeThenParam<T> = T extends PromiseLike<infer U> ? U : T
type GPLTP<T> = GetPromiseLikeThenParam<T>
// 測試
type T1 = GPLTP<typeof ajax1>
// ^^ = string
type T2 = GPLTP<typeof ajax4>
// ^^ = string
複製代碼
映射元組類型進行提取元組每個PromieLike
類型。
type ExtractTuplePromiseLike<T extends ReadonlyArray<unknown>> = {
[K in keyof T]: GPLTP<T[K]>
}
type ETPL<T extends ReadonlyArray<unknown>> = ExtractTuplePromiseLike<T>
type T1 = ETPL<typeof ajaxArr>
複製代碼
ReadonlyArray<unknown>
至關於 readonly unknown[]
。
基礎類型準備就緒,接下來就是寫函數聲明。
函數參數是一個元組,所以聲明參數爲ReadonlyArray<unknown>
,因爲返回的類型與函數參數有關,所以函數參數要聲明爲泛型T
,而後返回就是經過上面的ETPL
提取T
,而後再用Promise
包裝就成功寫好myPromiseAll
函數類型
declare function myPromiseAll<T extends ReadonlyArray<unknown>>(
tuple: T,
): Promise<ETPL<T>>
// 測試
myPromiseAll(ajaxArr)
.then((res) => {})
// ^^^ = readonly [string, number, boolean, string, number, true]
複製代碼
和Promise.all
的區別是多了個readonly
和變量使用時須要用到as const
,若是爲了方即可以這麼寫:
myPromiseAll([ajax1, ajax2, ajax3, ajax4, ajax5, ajax6] as const).then((res) => {})
複製代碼
也是徹底沒有問題。
myPromiseAll
相較於Promise.all
的類型仍是有些區別的,Promise.all
在數組長度超過10的時候會報錯而myPromiseAll
不會。
myPromiseAll
須要經過as const
進行參數聲明傳入元組,Promise.all
不須要。
myPromiseAll
返回的Promise<readonly [元組元素]
,Promise.all
返回的Promsie<[元組元素]
。
type GetPromiseLikeThenParam<T> = T extends PromiseLike<infer U> ? U : T
type GPLTP<T> = GetPromiseLikeThenParam<T>
type ExtractTuplePromiseLike<T extends ReadonlyArray<unknown>> = {
[K in keyof T]: GPLTP<T[K]>
}
type ETPL<T extends ReadonlyArray<unknown>> = ExtractTuplePromiseLike<T>
declare function myPromiseAll<T extends ReadonlyArray<unknown>>(
tuple: T,
): Promise<ETPL<T>>
複製代碼
人是菜雞,共同進步,若有錯誤,多多指教。