在這篇文章中,分享TypeScript中的5個不良實踐以及如何避免它們。
1. 將錯誤聲明為Any類型
示例
在以下代碼片段中,我們捕獲錯誤然后將其聲明為any類型。
async function asyncFunction() {
try {
const response = await doSomething();
return response;
} catch (err: any) {
toast(`Failed to do something: ${err.message}`);
}
}
為什么這是不好的 ?
沒有保證錯誤會有一個類型為字符串的message字段。
不幸的是,由于類型斷言,代碼讓我們假設它確實有。
代碼在開發環境中可以針對特定測試用例工作,但在生產環境中可能會嚴重失敗。
應該做什么替代方案 ?
不要設置錯誤類型。它應該默認為unknown
。
你可以做到以下幾點:
選項1: 使用類型守衛檢查錯誤是否為正確的類型。
async functionasyncFunction() {
try {
const response = awaitdoSomething();
return response;
} catch (err) {
const toastMessage = hasMessage(err)
? `Failed to do something: ${err.message}`
: `Failed to do something`;
toast(toastMessage);
}
}
// 我們使用類型守衛首先進行檢查
functionhasMessage(value: unknown): value is { message: string } {
return (
value != null &&
typeof value === "object" &&
"message"in value &&
typeof value.message === "string"
);
}
// 你也可以簡單地檢查錯誤是否是Error的實例
const toastMessage = err instanceofError
? `Failed to do something: ${err.message}`
: `Failed to do something`;
選項2(推薦): 不要對錯誤類型做出假設
與其對錯誤類型做出假設,不如明確處理每種類型并為用戶提供適當的反饋。
如果你無法確定具體的錯誤類型,最好顯示完整的錯誤信息而不是部分細節。
有關錯誤處理的更多信息,請查看這篇出色的指南:編寫更好的錯誤消息。
2. 函數中有多個連續的相同類型參數
示例
export function greet(
firstName: string,
lastName: string,
city: string,
email: string
) {
// 做一些事情...
}
為什么這是不好的 ?
// 我們顛倒了firstName和lastName,但TypeScript不會捕獲這一點
greet("Curry", "Stephen", "LA", "stephen.curry@gmail.com");
- 在代碼審查中,尤其是在看到函數調用之前,更難以理解每個參數代表什么。
應該做什么替代方案 ?
使用對象參數來明確每個字段的目的,并最小化錯誤的風險。
export function greet(params: {
firstName: string;
lastName: string;
city: string;
email: string;
}) {
// 做一些事情...
}
3. 函數有多個分支且沒有返回類型
示例
function getAnimalDetails(animalType: "dog" | "cat" | "cow") {
switch (animalType) {
case"dog":
return { name: "Dog", sound: "Woof" };
case"cat":
return { name: "Cat", sound: "Meow" };
case"cow":
return { name: "Cow", sound: "Moo" };
default:
// 這確保TypeScript捕獲未處理的案例
((_: never) => {})(animalType);
}
}
為什么這是不好的 ?
- 添加新的
animalType
可能導致返回結構不正確的對象。 - 返回類型結構的更改可能會導致代碼的其他部分出現難以追蹤的問題。
應該做什么替代方案 ?
明確指定函數的返回類型:
type Animal = {
name: string;
sound: string;
};
functiongetAnimalDetails(animalType: "dog" | "cat" | "cow"): Animal {
switch (animalType) {
case"dog":
return { name: "Dog", sound: "Woof" };
case"cat":
return { name: "Cat", sound: "Meow" };
case"cow":
return { name: "Cow", sound: "Moo" };
default:
// 這確保TypeScript捕獲未處理的案例
((_: never) => {})(animalType);
}
}
4. 添加不必要的類型而不是使用可選字段
示例
type Person = {
name: string;
age: number;
};
typePersonWithAddress = Person & {
address: string;
};
typePersonWithAddressAndEmail = PersonWithAddress & {
email: string;
};
typePersonWithEmail = Person & {
email: string;
};
為什么這是不好的 ?
應該做什么替代方案 ?
使用可選字段來保持你的類型簡單和可維護:
type Person = {
name: string;
age: number;
address?: string;
email?: string;
};
5. 在不同組件級別使屬性變為可選
示例
interface TravelFormProps {
disabled?: boolean;
}
exportfunctionTravelForm(props: TravelFormProps) {
// 使用日期范圍選擇器組件...
}
interfaceDateRangePickerProps {
disabled?: boolean;
}
functionDateRangePicker(props: DateRangePickerProps) {
// 使用日期選擇器組件...
}
interfaceDatePickerProps {
disabled?: boolean;
}
functionDatePicker(props: DatePickerProps) {}
為什么這是不好的 ?
- 容易忘記傳遞
disabled
屬性,導致部分啟用的表單。
應該做什么替代方案 ?
使共享字段在內部組件中必需。
這將確保正確的屬性傳遞。這對于低級別的組件尤其重要,以便盡早捕獲任何疏忽。
在上面的例子中,disabled
現在在所有內部組件中都是必需的。
interface TravelFormProps {
disabled?: boolean;
}
exportfunctionTravelForm(props: TravelFormProps) {
// 使用日期范圍選擇器組件...
}
interfaceDateRangePickerProps {
disabled: boolean | undefined;
}
functionDateRangePicker(props: DateRangePickerProps) {
// 使用日期選擇器組件...
}
interfaceDatePickerProps {
disabled: boolean | undefined;
}
functionDatePicker(props: DatePickerProps) {}
注意:如果你正在為庫設計組件,我不推薦這樣做,因為必需字段需要更多的工作。
總結
TypeScript是令人驚嘆的,但沒有工具???是完美的。
避免這5個錯誤將幫助你編寫更干凈、更安全、更易于維護的代碼。
閱讀原文:原文鏈接
該文章在 2025/1/10 11:09:59 編輯過