keyof
功能加強 keyof
和映射類型支持 number
和 symbol
類型的屬性名稱。node
在索引類型和映射類型中,TypeScript 2.9 新增了對 number
和 symbol
類型屬性名稱的支持。在以前的版本,keyof
和映射類型只支持 string
類型的屬性名稱。git
新功能帶來的變化包括:github
T
,keyof T
的索引類型是 string | number | symbol
的子類型{ [P in K]: XXX }
會映射全部兼容 string | number | symbol
的 K
for...in
語句中泛型爲 T
的對象,迭代變量的類型在以前推斷爲 keyof T
,可是如今推斷爲 Extract<keyof T, string>
(即,只包含 keyof T
中的類字符串值)給定一個對象類型 X
,keyof X
的類型求值過程以下:json
X
包含字符串索引簽名,那麼 keyof X
的值爲 string
、number
和類符號屬性名的字面量類型組成的聯合類型,不然下一步X
包含數值索引簽名,那麼 keyof X
的值爲 number
和類字符串與類符號屬性名的字面量類型組成的聯合類型,不然下一步keyof X
的值爲類字符串、類數值和類符號屬性名的字面量類型組成的聯合類型其中,瀏覽器
在映射類型 { [P in K]: XXX }
中,K
中的每個字符串字面量類型都會引入一個字符串名稱的類型,每個數值類型字面量類型都會引入一個數值名稱的類型,每個符號類型都會引入一個符號名稱的類型。此外,若是 K
包含 string
類型,那麼會引入一個字符串索引簽名,若是包含 number
類型,也會引入一個數值索引簽名。模塊化
上面的文字可能不太好理解,來看看下面的例子:函數
const c = "c"; const d = 10; const e = Symbol(); const enum E1 { A, B, C } const enum E2 { A = "A", B = "B", C = "C" } type Foo = { a: string; // string-like,標識符 5: string; // number-like, 數值字面量 [c]: string; // string-like, 字符串字面量類型的計算屬性 [d]: string; // number-like, 數值字面量類型的計算屬性 [e]: string; // symbol-like, 符號類型的計算屬性 [E1.A]: string; // number-like, 數值枚舉類型的計算屬性 [E2.A]: string; // string-like, 字符串枚舉類型的計算屬性 } type K1 = keyof Foo; // "a" | 5 | "c" | 10 | typeof e | E1.A | E2.A; type K2 = Extract<keyof Foo, string>; // "a" | "c" | E2.A type K3 = Extract<keyof Foo, number>; // 5 | 10 | E1.A type K4 = Extract<keyof Foo, symbol>; // typeof e
如今,因爲 keyof
能夠經過在鍵名類型中使用 number
來映射到數值索引簽名,因此諸如 Partial<T>
和 Readonly<T>
的映射類型可以正確地處理對象類型中的數值索引簽名了。es5
type Arrayish<T> = { length: number; [x: number]: T; } type ReadonlyArrayish<T> = Readonly<Arrayish<T>>; declare const map: ReadonlyArrayish<string>; let n = map.length; let x = map[123]; // x 推斷爲 string,以前版的版本會推斷爲 any
此外,得益於 keyof
操做支持使用 number
和 symbol
命名的鍵值,當一個對象使用數值字面量(如數值枚舉類型)和惟一符號做爲屬性索引時,如今咱們能夠把訪問對象屬性的過程獨立地抽象出來了。命令行
const enum Enum { A, B, C } const enumToStringMap = { [Enum.A]: "Name A", [Enum.B]: "Name B", [Enum.C]: "Name C" } const sym1 = Symbol(); const sym2 = Symbol(); const sym3 = Symbol(); const symbolToNumberMap = { [sym1]: 1, [sym2]: 2, [sym3]: 3 }; type KE = keyof typeof enumToStringMap; // Enum (i.e. Enum.A | Enum.B | Enum.C) type KS = keyof typeof symbolToNumberMap; // typeof sym1 | typeof sym2 | typeof sym3 function getValue<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } let x1 = getValue(enumToStringMap, Enum.C); // x1 類型爲 string ,返回 "Name C" let x2 = getValue(symbolToNumberMap, sym3); // x1 類型爲 number ,返回 3
這是一項重大功能變動。在以前的版本中,keyof
和映射類型只支持 string
類型命名的屬性。如今,若是還認爲 keyof T
的類型值老是 string
的話,那麼將會拋出錯誤,由於此時 keyof T
的類型值爲 string | number | symbol
。舉例以下:debug
function useKey<T, K extends keyof T>(o: T, k: K) { var name: string = k; // Error: keyof T is not assignable to string }
針對該重大功能變動,有如下建議:
string
類屬性名,那麼可使用 Extract<keyof T, string>
:function useKey<T, K extends Extract<keyof T, string>>(o: T, k: K) { var name: string = k; // OK }
function useKey<T, K extends keyof T>(o: T, k: K) { var name: string | number | symbol = k; }
--keyofStringsOnly
編譯選項來禁用新功能如今,JSX 支持傳遞泛型參數給泛型組件了。
class GenericComponent<P> extends React.Component<P> { internalProp: P; } type Props = { a: number; b: string; }; const x = <GenericComponent<Props> a={10} b="hi"/>; // OK const y = <GenericComponent<Props> a={10} b={20} />; // Error
標籤模板是 ES2015 引入的一種新調用方式。跟調用表達式同樣,泛型函數也可用在標籤模板中,TypeScript 會根據類型參數進行類型推斷。
TypeScript 2.9 支持向標籤模板字符串傳遞泛型類型參數。
declare function styledComponent<Props>(strs: TemplateStringsArray): Component<Props>; interface MyProps { name: string; age: number; } styledComponent<MyProps> ` font-size: 1.5em; text-align: center; color: palevioletred; `; declare function tag<T>(strs: TemplateStringsArray, ...args: T[]): T; // a 的類型爲 string | number let a = tag<string | number> `${100} ${"hello"}`;
import
類型模塊能夠導入其餘模塊的類型聲明。可是,非模塊化的全局腳本沒法訪問模塊的類型聲明。import
爲此打開了新世界的大門。
經過在類型註釋中使用 import("mod")
,咱們能夠訪問到該模塊導出的類型聲明,同時模塊代碼也並不會所以而引入。
來看一個簡單的例子。
假如模塊中有如下聲明:
// module.d.ts export declare class Pet { name: string; }
那麼就能夠在非模塊化的全局腳本中按如下方式使用它:
// global-script.ts function adopt(p: import("./module").Pet) { console.log(`Adopting ${p.name}...`); }
也能夠在 JSDoc 註釋中使用它:
// a.js /** * @param p { import("./module").Pet } */ function walk(p) { console.log(`Walking ${p.name}...`); }
消除聲明錯誤
支持 import
類型後,聲明文件生成過程當中拋出的許多錯誤能夠由編譯器直接處理,而不須要改變原輸入文件。
例如:
import { createHash } from "crypto"; export const hash = createHash("sha256"); // ^^^^ // 導出變量 'hash' 已經或正在使用外部模塊 "crypto" 中的名稱 'Hash',但沒法對其命名
在 TypeScript 2.9 中,這樣的錯誤就不會拋出來了,而且生成的聲明文件以下:
export declare const hash: import("crypto").Hash;
import.meta
TypeScript 2.9 引入了對 import.meta
的支持,它是 TC39 提案中的一種新的元屬性(meta-property)。
import.meta
的類型由全局類型 ImportMeta
所定義,位於 lib.es5.d.ts
。該接口的使用範圍是很受限的,主要用來爲 Node 或者瀏覽器添加衆所周知的屬性,以及可能根據上下文進行的全局加強。
例如,假設 __dirname
在 import.meta
中老是可用的,那麼就能夠經過 ImportMeta
接口來新增該屬性:
// node.d.ts interface ImportMeta { __dirname: string; }
使用方法以下:
import.meta.__dirname // 類型爲 'string'
import.meta
只能在編譯輸出爲 ESNext
模塊和 ECMAScript 時使用。
--resolveJsonModule
在 Node.js 應用中,一般都會使用 .json
文件。在 TypeScript 2.9 中,--resolveJsonModule
編譯選項可容許從 .json
中導入、導出其類型。
// settings.json { "repo": "TypeScript", "dry": false, "debug": false }
import settings from "./settings.json"; settings.debug === true; // OK settings.dry === 2; // Error: '===' 不能用於比較 boolean 和 number 類型
// tsconfig.json { "compilerOptions": { "module": "commonjs", "resolveJsonModule": true, "esModuleInterop": true } }
--pretty
自 TypeScript 2.9 開始,若是輸出設備支持多顏色文本,錯誤信息將默認開啓 --pretty
選項。TypeScript 會自動檢查輸出流是否設置 isTty
屬性。
能夠在命令行使用 --pretty false
或者 tsconfig.json
中設置 "pretty": false
來禁用 --pretty
輸出。
--declarationMap
若是在開啓 --declaration
的狀況下,同時開啓 --declarationMap
,那麼編譯器會同時生成 .d.ts
和 .d.ts.map
文件。語言服務如今可以正確識別這些映射文件,而且使用它們來映射到源碼。
也就是說,在使用「跳到定義之處」功能時,會直接跳轉到源碼文件,而不是 .d.ts
文件。