假設這樣一個場景,目前業務上僅對接了三方支付 'Alipay', 'Wxpay', 'PayPal'
, 實際業務 getPaymentMode
會根據不一樣支付方式進行不一樣的付款/結算流程。html
const PAYMENT_MODE = ['Alipay', 'Wxpay', 'PayPal']; function getPaymentMode(paymode: string) { return PAYMENT_MODE.find(thirdPay => thirdPay === paymode) } getPaymentMode('Alipay') // ✔️ getPaymentMode('Wxpay') // ✔️ getPaymentMode('PayPal') // ✔️ getPaymentMode('unknow') // ✔️ 正常編譯,但可能引起運行時邏輯錯誤
因爲聲明僅約束了入參 string
類型,沒法避免因爲手誤或上層業務處理傳參不當引發的運行時邏輯錯誤。git
能夠經過聲明字面量聯合類型來解決上述問題。github
const PAYMENT_MODE = ['Alipay', 'Wxpay', 'PayPal']; type mode = 'Alipay' | 'Wxpay' | 'PayPal'; function getPaymentMode(paymode: mode) { return PAYMENT_MODE.find(thirdPay => thirdPay === paymode) } getPaymentMode('Alipay') // ✔️ getPaymentMode('Wxpay') // ✔️ getPaymentMode('PayPal') // ✔️ getPaymentMode('unknow') // ❌ Argument of type '"unknow"' is not assignable to parameter of type 'mode'.(2345)
字面量聯合類型雖然解決了問題,可是須要保持值數組和聯合類型之間的同步,且存在冗餘。typescript
二者聲明在同一個文件時,問題尚且不大。若 PAYMENT_MODE
由第三方庫提供,對方非 TypeScript
技術棧沒法提供類型文件,那要保持同步就比較困難,新增支付類型或支付渠道合做終止,都會引入潛在風險。數組
const PAYMENT_MODE = ['Alipay', 'Wxpay', 'PayPal'] as const; //亦可 import { PAYMENT_MODE } from 'outer' type mode = typeof PAYMENT_MODE[number] // "Alipay" | "Wxpay" | "PayPal" 1) function getPaymentMode(paymode: mode) { return PAYMENT_MODE.find(thirdPay => thirdPay === paymode) } getPaymentMode('Alipay') // ✔️ getPaymentMode('Wxpay') // ✔️ getPaymentMode('PayPal') // ✔️ getPaymentMode('unknow') // ❌ Argument of type '"unknow"' is not assignable to parameter of type '"Alipay" | "Wxpay" | "PayPal"'.
1)處引入了本文的主角 typeof ArrayInstance[number]
完美的解決了上述問題,經過數組值獲取對應類型。ide
typeof ArrayInstance[number] 如何拆解
首先能夠肯定 type mode = typeof PAYMENT_MODE[number]
在 TypeScript
類型聲明上下文 ,而非 JavaScript
變量聲明上下文。函數
PAYMENT_MODE
是數組實例,number
是 TypeScript
數字類型。如果 PAYMENT_MODE[number]
組合,則語法不正確,數組實例索引操做 []
中只能具體數字, 不能是類型。url
因此 typeof PAYMENT_MODE[number]
等同於 (typeof PAYMENT_MODE)[number]
。es5
能夠看出 typeof PAYMENT_MODE
是一個數組類型spa
type mode1 = typeof PAYMENT_MODE // readonly ["Alipay", "Wxpay", "PayPal"]
typeof PAYMENT_MODE[number] 等效 mode1[number]
,咱們知道 mode1[]
是 indexed access types
,[]
中 Index
來源於 Index Type Query
也即 keyof
操做 。
type mode1 =keyof typeof PAYMENT_MODE // number | "0" | "1" | "2" | "length" | "toString" | "toLocaleString" | "concat" | "join" | "slice" | "indexOf" | "lastIndexOf" | "every" | "some" | "forEach" | "map" | "filter" | ... 7 more ... | "includes"
能夠看出獲得的聯合類型第一項就是 number
類型,咱們常見 keyof
獲得的都是類型屬性名組成的字符串字面量聯合類型,以下所示,那這個 number
是怎麼來的。
interface Person { name: string; age: number; location: string; } type K1 = keyof Person; // "name" | "age" | "location"
從 TypeScript-2.9 文檔能夠看出,
若是 X 是對象類型, keyof X 解析規則以下:
- 若是 X 包含字符串索引簽名, keyof X 則是由string 、number 類型, 以及symbol-like 屬性字面量類型組成的聯合類型, 不然
- 若是 X 包含數字索引簽名, keyof X 則是由number類型 , 以及string-like 、symbol-like 屬性字面量類型組成的聯合類型, 不然
- keyof X 由 string-like, number-like, and symbol-like 屬性字面量類型組成的聯合類型.
其中
- 對象類型的 string-like 屬性能夠是 an identifier, a string literal, 或者 string literal type的計算屬性名 .
- 對象類型的number-like 屬性能夠是 a numeric literal 或 numeric literal type 的計算屬性名.
- 對象類型的symbol-like 屬性能夠是a unique symbol type的計算屬性名.
示例以下:
const c = "c1"; const d = 10; const e = Symbol(); const enum E1 { A } const enum E2 { A = "A" } type Foo1 = { "f": string, // String-like 中 a string literal ["g"]:string; // String-like 中 計算屬性名 a: string; // String-like 中 identifier [c]: string; // String-like 中 計算屬性名 [E2.A]: string; // String-like 中計算屬性名 5: string; // Number-like 中 numeric literal [d]: string; // Number-like 中 計算屬性名 [E1.A]: string; // Number-like 中 計算屬性名 [e]: string; // Symbol-like 中 計算屬性名 }; type K11 = keyof Foo1; // type K11 = "c1" | E2.A | 10 | E1.A | typeof e | "f" | "g" | "a" | 5
再次回到前面內容:
type payType = typeof PAYMENT_MODE; // readonly ["Alipay", "Wxpay", "PayPal" type mode1 =keyof typeof PAYMENT_MODE // number | "0" | "1" | "2" | "length" | "toString" | "toLocaleString" | "concat" | "join" | "slice" | "indexOf" | "lastIndexOf" | "every" | "some" | "forEach" | "map" | "filter" | ... 7 more ... | "includes"
編譯器提示的 readonly ["Alipay", "Wxpay", "PayPal"
類型不夠具象,咱們無從得知 payType
具體有哪些屬性。
keyof typeof PAYMENT_MODE
只有 number
類型而沒有 string
類型,根據上面 keyof
解析規則的第2條,能夠推斷 typeof PAYMENT_MODE
類型含有數字索引簽名,以及以前的結果 type mode = typeof PAYMENT_MODE[number] // "Alipay" | "Wxpay" | "PayPal"
。
咱們能夠據此推測出 payType
更加直觀的類型結構:
type payType = { [i :number]: "Alipay" | "Wxpay" | "PayPal"; //數字索引簽名 "length": number; "0": "Alipay"; //由於數組能夠經過數字或字符串訪問 "1": "Wxpay"; .... "toString": Function; //省略其他數組方法屬性 ..... } type eleType = payType[number] // "Alipay" | "Wxpay" | "PayPal"
後來我在 lib.es5.d.ts 中找到了 ReadonlyArray<T> 類型,更進一步驗證了上面的推測:
interface ReadonlyArray<T> { readonly length: number; toString(): string; //......省略中間函數 readonly [n: number]: T; }
payType
中多出的 "0", "1"等字符串索引屬性猜想是因爲 as const
斷言爲常量類型,編譯期自動生成的,相關實現還未找到,往後遇到文檔再補充。
藉助 typeof ArrayInstance[number]
從常量值數組中獲取對應元素字面量類型 的剖析至此結束 。