!
)本章討論 TypeScript 中的類型斷言,這與其他語言中的類型轉換相關,並透過 as
運算子執行。
類型斷言讓我們可以覆寫 TypeScript 為值計算出的靜態類型。這對於解決類型系統的限制很有用。
類型斷言與其他語言中的類型轉換相關,但它們不會擲回例外,也不會在執行階段執行任何動作(它們會在靜態時執行一些基本檢查)。
: object = ['a', 'b', 'c']; // (A)
const data
// @ts-expect-error: Property 'length' does not exist on type 'object'.
.length; // (B)
data
.equal(
assertas Array<string>).length, 3); // (C) (data
註解
在 A 行中,我們將陣列的類型擴充為 object
。
在 B 行中,我們看到此類型不讓我們存取任何屬性(詳細資訊)。
在 C 行中,我們使用類型斷言(運算子 as
)告訴 TypeScript,data
是陣列。現在我們可以存取屬性 .length
。
類型斷言是最後的選擇,應盡量避免。它們(暫時)移除了靜態類型系統通常給我們的安全網。
請注意,在 A 行中,我們也覆寫了 TypeScript 的靜態類型。但我們透過類型註解來執行此動作。這種覆寫方式比類型斷言安全得多,因為我們的限制更多:TypeScript 的類型必須可以指定給註解的類型。
TypeScript 有類型斷言的替代「尖括號」語法
<Array<string>>data
我建議避免使用此語法。它已經過時,且與 React JSX 程式碼(在 .tsx
檔案中)不相容。
為了存取任意物件 obj
的屬性 .name
,我們暫時將 obj
的靜態類型變更為 Named
(A 行和 B 行)。
interface Named {: string;
name
}function getName(obj: object): string {
if (typeof (obj as Named).name === 'string') { // (A)
return (obj as Named).name; // (B)
}'(Unnamed)';
return }
在下列程式碼中(A 行),我們使用類型宣告 as Dict
,以便存取推論類型為 object
的值的屬性。也就是說,我們使用靜態類型 Dict
覆寫靜態類型 object
。
= {[k:string]: any};
type Dict
function getPropertyValue(dict: unknown, key: string): any {
if (typeof dict === 'object' && dict !== null && key in dict) {
// %inferred-type: object
;
dict
// @ts-expect-error: Element implicitly has an 'any' type because
// expression of type 'string' can't be used to index type '{}'.
// [...]
;
dict[key]
return (dict as Dict)[key]; // (A)
else {
} throw new Error();
} }
!
)如果值的類型是包含類型 undefined
或 null
的聯集,非空斷言運算子(或非空斷言運算子)會從聯集中移除這些類型。我們告訴 TypeScript:「這個值不能是 undefined
或 null
。」因此,我們可以執行這些兩個值類型所禁止的運算,例如
= 'Jane' as (null | string);
const theName
// @ts-expect-error: Object is possibly 'null'.
.length;
theName
.equal(
assert!.length, 4); // OK theName
.has()
之後的 .get()
在我們使用 Map 方法 .has()
之後,我們知道 Map 有給定的金鑰。唉,.get()
的結果並未反映該知識,這就是我們必須使用非空斷言運算子的原因
function getLength(strMap: Map<string, string>, key: string): number {
if (strMap.has(key)) {
// We are sure x is not undefined:
= strMap.get(key)!; // (A)
const value .length;
return value
}-1;
return }
如果 Map 的值不能是 undefined
,我們可以避免使用非空斷言運算子。然後,可以透過檢查 .get()
的結果是否為 undefined
來偵測遺失的項目
function getLength(strMap: Map<string, string>, key: string): number {
// %inferred-type: string | undefined
= strMap.get(key);
const value if (value === undefined) { // (A)
-1;
return
}
// %inferred-type: string
;
value
.length;
return value }
如果已開啟嚴格屬性初始化,我們偶爾需要告訴 TypeScript 我們確實初始化某些屬性,即使它認為我們沒有。
這是 TypeScript 抱怨的範例,即使它不應該這樣做
class Point1 {// @ts-expect-error: Property 'x' has no initializer and is not definitely
// assigned in the constructor.
: number;
x
// @ts-expect-error: Property 'y' has no initializer and is not definitely
// assigned in the constructor.
: number;
y
constructor() {
.initProperties();
this
}initProperties() {
.x = 0;
this.y = 0;
this
} }
如果我們在 A 行和 B 行使用明確指定宣告(驚嘆號),錯誤就會消失
class Point2 {!: number; // (A)
x!: number; // (B)
yconstructor() {
.initProperties();
this
}initProperties() {
.x = 0;
this.y = 0;
this
} }