該做和不該做的事

一般類型

數字字串布林符號物件

請勿使用類型數字字串布林符號物件。這些類型是指非原始的封裝物件,在 JavaScript 程式碼中幾乎從未使用得恰當。

ts
/* WRONG */
function reverse(s: String): String;

使用類型numberstringbooleansymbol

ts
/* OK */
function reverse(s: string): string;

請勿使用物件,請使用非原始的object類型(新增於 TypeScript 2.2)。

泛型

請勿使用未套用其類型參數的泛型類型。請參閱 TypeScript 常見問題解答頁面 以取得更多詳細資訊。

any

請勿使用any作為類型,除非您正在將 JavaScript 專案移轉至 TypeScript。編譯器實際上any視為「請關閉此項目的類型檢查」。這類似於在變數的每個使用位置加上@ts-ignore註解。當您首次將 JavaScript 專案移轉至 TypeScript 時,這可能非常有幫助,因為您可以將尚未移轉的項目設定為any類型,但在完整的 TypeScript 專案中,您會停用使用它的程式任何部分的類型檢查。

在不知道要接受什麼類型,或想接受任何東西(因為會盲目傳遞而不會與之互動)時,可以使用 unknown

回呼類型

回呼的傳回類型

不要 對會忽略其值的回呼使用 any 傳回類型

ts
/* WRONG */
function fn(x: () => any) {
x();
}

對會忽略其值的回呼使用 void 傳回類型

ts
/* OK */
function fn(x: () => void) {
x();
}

原因: 使用 void 較安全,因為它可以防止意外以未檢查的方式使用 x 的傳回值

ts
function fn(x: () => void) {
var k = x(); // oops! meant to do something else
k.doSomething(); // error, but would be OK if the return type had been 'any'
}

回呼中的選用參數

不要 在回呼中使用選用參數,除非你真的有此意

ts
/* WRONG */
interface Fetcher {
getObject(done: (data: unknown, elapsedTime?: number) => void): void;
}

這有非常具體的意義:done 回呼可能會使用 1 個參數呼叫,也可能會使用 2 個參數呼叫。作者可能想說回呼可能不關心 elapsedTime 參數,但不需要將參數設為選用以達成此目的 — 始終可以提供接受較少參數的回呼。

將回呼參數寫成非選用

ts
/* OK */
interface Fetcher {
getObject(done: (data: unknown, elapsedTime: number) => void): void;
}

過載和回呼

請勿撰寫僅在回呼 arity 上有差異的個別過載

ts
/* WRONG */
declare function beforeAll(action: () => void, timeout?: number): void;
declare function beforeAll(
action: (done: DoneFn) => void,
timeout?: number
): void;

使用最大 arity 撰寫單一過載

ts
/* OK */
declare function beforeAll(
action: (done: DoneFn) => void,
timeout?: number
): void;

原因:回呼總是合法地忽略參數,因此不需要較短的過載。優先提供較短的回呼允許傳遞輸入類型錯誤的函式,因為它們符合第一個過載。

函式過載

排序

請勿將較通用的過載置於較具體的過載之前

ts
/* WRONG */
declare function fn(x: unknown): unknown;
declare function fn(x: HTMLElement): number;
declare function fn(x: HTMLDivElement): string;
var myElem: HTMLDivElement;
var x = fn(myElem); // x: unknown, wat?

依序將較通用的簽章置於較具體的簽章之後,來排序過載

ts
/* OK */
declare function fn(x: HTMLDivElement): string;
declare function fn(x: HTMLElement): number;
declare function fn(x: unknown): unknown;
var myElem: HTMLDivElement;
var x = fn(myElem); // x: string, :)

原因:TypeScript 在解析函式呼叫時會選擇第一個符合的過載。當較早的過載「較通用」於較晚的過載時,較晚的過載實際上會被隱藏且無法呼叫。

使用選用參數

請勿撰寫僅在尾端參數不同的多個重載

ts
/* WRONG */
interface Example {
diff(one: string): number;
diff(one: string, two: string): number;
diff(one: string, two: string, three: boolean): number;
}

盡可能使用可選參數

ts
/* OK */
interface Example {
diff(one: string, two?: string, three?: boolean): number;
}

請注意,只有當所有重載具有相同的回傳類型時,才應執行此合併。

原因:這很重要,原因有兩個。

TypeScript 會透過查看目標的任何簽章是否可以使用來源的引數來呼叫,來解析簽章相容性,且允許額外的引數。例如,此程式碼僅在使用可選參數正確撰寫簽章時才會公開錯誤

ts
function fn(x: (a: string, b: number, c: number) => void) {}
var x: Example;
// When written with overloads, OK -- used first overload
// When written with optionals, correctly an error
fn(x.diff);

第二個原因是使用者使用 TypeScript 的「嚴格 null 檢查」功能時。由於未指定參數在 JavaScript 中顯示為 undefined,因此通常可以將明確的 undefined 傳遞給具有可選引數的函式。例如,此程式碼在嚴格 null 檢查下應為正常

ts
var x: Example;
// When written with overloads, incorrectly an error because of passing 'undefined' to 'string'
// When written with optionals, correctly OK
x.diff("something", true ? undefined : "hour");

使用聯合類型

請勿撰寫僅在一個引數位置類型不同的重載

ts
/* WRONG */
interface Moment {
utcOffset(): number;
utcOffset(b: number): Moment;
utcOffset(b: string): Moment;
}

盡可能使用聯合類型

ts
/* OK */
interface Moment {
utcOffset(): number;
utcOffset(b: number | string): Moment;
}

請注意,我們在此未將 b 設為可選,因為簽章的回傳類型不同。

原因:這對於「傳遞」值給函式的人來說很重要

ts
function fn(x: string): Moment;
function fn(x: number): Moment;
function fn(x: number | string) {
// When written with separate overloads, incorrectly an error
// When written with union types, correctly OK
return moment().utcOffset(x);
}

TypeScript 文件是一個開放原始碼專案。透過 傳送 Pull Request ❤ 來協助我們改善這些頁面

此頁面的貢獻者
MHMohamed Hegazy (55)
OTOrta Therox (15)
MZMicah Zoltu (3)
MFMartin Fischer (1)
JJongbin (1)
15+

最後更新:2024 年 3 月 21 日