做者:Dr. Axel Rauschmayer翻譯:瘋狂的技術宅html
原文:https://2ality.com/2020/06/ty...前端
未經容許嚴禁轉載程序員
本文是關於 TypeScript 中的 type assertions 的,它與其餘語言中的類型強制轉換有類似之處,並經過 as
運算符執行。面試
類型斷言使咱們能夠覆蓋 TypeScript 爲存儲位置計算的靜態類型,這對於解決類型系統的限制頗有用。typescript
類型斷言與其餘語言中的類型強制轉換有類似之處,可是它們不會引起異常,而且在運行時也不作任何事情(它們確實會靜態執行一些少許的檢查)。segmentfault
const data: object = ['a', 'b', 'c']; // (A) // @ts-ignore: Property 'length' does not exist on type 'object'. data.length; // (B) assert.equal( (data as Array<string>).length, 3); // (C)
object
。as
)告訴 TypeScript data
是一個Array。如今就能夠訪問屬性 .length
了。類型斷言是不得已的方法,應儘量的避免。他們(暫時)刪除了靜態類型系統爲咱們提供的安全網。安全
注意,在 A 行中,咱們還覆蓋了 TypeScript 的靜態類型,不過是經過類型註釋完成的。這種覆蓋方式比類型聲明要安全得多,由於你能夠作的事情少得多。 TypeScript 的類型必須可以分配給註釋的類型。服務器
TypeScript 對於類型斷言有另外一種「尖括號」語法:微信
<Array<string>>data
該語法已通過時,而且與 React JSX 代碼(在 .tsx
文件中)不兼容。多線程
爲了訪問任意對象 obj
的屬性 .name
,咱們暫時將 obj
的靜態類型更改成 Named
(A行和B行)。
interface Named { name: string; } function getName(obj: object): string { if (typeof (obj as Named).name === 'string') { // (A) return (obj as Named).name; // (B) } return '(Unnamed)'; }
在如下代碼中,咱們在行 A 用了類型斷言 as Dict
,以即可以訪問其推斷類型爲 object
的值的屬性。也就是說,用靜態類型 Dict
覆蓋了推斷的靜態類型 object
。
type Dict = {[k:string]: any}; function getPropertyValue(dict: unknown, key: string): any { if (typeof dict === 'object' && dict !== null && key in dict) { // %inferred-type: object dict; // @ ts-ignore:元素隱式具備「any」類型,由於 // 類型'string'的表達式不能用於索引類型'{}'。 // 在類型「 {}」上沒有找到參數類型爲'string'的索引簽名。 dict[key]; return (dict as Dict)[key]; // (A) } else { throw new Error(); } }
!
)若是值的類型是包含 undefined
或 null
類型的聯合,則 non-nullish聲明運算符(或 non-null 聲明運算符)將從聯合中刪除這些類型。咱們告訴 TypeScript:「這個值不能是 undefined
或 null
。」所以,咱們能夠執行這兩個值的類型所阻止的操做,例如:
const theName = 'Jane' as (null | string); // @ts-ignore: Object is possibly 'null'. theName.length; assert.equal( theName!.length, 4); // OK
.has()
以後的 .get()
使用 Map 方法 .has()
以後,咱們知道 Map 具備給定的鍵。遺憾的是,.get()
的結果不能反映這一點,這就是爲何咱們必須使用 nullish 斷言運算符的緣由:
function getLength(strMap: Map<string, string>, key: string): number { if (strMap.has(key)) { // We are sure x is not undefined: const value = strMap.get(key)!; // (A) return value.length; } return -1; }
因爲 strMap
的值永遠不會是 undefined
,所以咱們能夠經過檢查 .get()
的結果是否爲 undefined
來檢測丟失的 Map 條目(A 行):
function getLength(strMap: Map<string, string>, key: string): number { // %inferred-type: string | undefined const value = strMap.get(key); if (value === undefined) { // (A) return -1; } // %inferred-type: string value; return value.length; }
若是打開 strict 屬性初始化,有時須要告訴 TypeScript 咱們確實初始化某些屬性——即便它認爲咱們不須要這樣作。
這是一個例子,儘管 TypeScript 不該這樣作,但它仍會報錯:
class Point1 { // @ts-ignore: Property 'x' has no initializer and is not definitely // assigned in the constructor. x: number; // @ts-ignore: Property 'y' has no initializer and is not definitely // assigned in the constructor. y: number; constructor() { this.initProperties(); } initProperties() { this.x = 0; this.y = 0; } }
若是咱們在 A 行和 B 行中使用「定值分配斷言」(感嘆號),則錯誤會消失:
class Point2 { x!: number; // (A) y!: number; // (B) constructor() { this.initProperties(); } initProperties() { this.x = 0; this.y = 0; } }