原文鏈接:https://www.totaltypescript.com/dont-use-function-keyword-in-typescript
翻譯:一川
在Typescript中不應該使用Function
作為一個類型,因為它可以表示任何函數。通常,我們期望的是更具體的類型--例如指定參數的數量或函數返回的內容。如果確實要表示可以接受任意數量的參數并返回任何類型的函數,請使用 (...args: any[]) => any
。
1完整的解釋
假設您正在創建一個匯總對象數組的函數。這是一個,取自Excalidraw代碼庫:
const sum = <T>(
array: readonly T[],
mApper: (item: T) => number
): number =>
array.reduce(
(acc, item) => acc + mapper(item),
0
);
讓我們看一下類型定義。此函數包含:
-
只讀數組:readonly T[] -
映射器函數:(item: T) => number
并返回 number
類型。
在主體中,它調用array.reduce(func, 0)
。這意味著在acc
對于數組的每個成員,它通過將acc
和mapper(item)
進行相加,最終得到數組所有成員的總和。
2什么可以用來作為函數聲明?
mapper
函數是關鍵。我們來剝離一下來看看:
type Mapper<T> = (item: T) => number;
讓我們想象一個用例:
interface YouTubeVideo {
name: string;
views: number;
}
const youTubeVideos: YouTubeVideo[] = [
{
name: "My favorite cheese",
views: 100,
},
{
name: "My second favorite cheese (you won't believe it)",
views: 67,
},
];
const mapper: Mapper<YouTubeVideo> = (video) => {
return video.views;
};
const result = sum(youTubeVideos, mapper); // 167
3關于什么是函數?
我看到很多初學者開發人員犯的一個大錯誤,聲明一個類似于 mapper和Function類型 的函數:
const sum = <T>(
array: readonly T[],
mapper: Function
): number =>
array.reduce(
(acc, item) => acc + mapper(item),
0
);
這個關鍵字基本上代表“任何函數”。這意味著從技術上講可以 sum 接收任何函數。
當在 中使用 sum 時,我們失去了很多 (item: T) => number
提供的安全性:
const result = sum(youTubeVideos, (item) => {
// Parameter 'item' implicitly has an 'any' type.
// We can return anything from here, not just
// a number!
return item.name;
});
TypeScript 現在無法推斷應該是什么 item ,或者我們的mapper函數應該返回什么。
這里的教訓是“不要使用 Function ” - 總有一個更具體的選項可用。
4表示“任何函數”
有時,期望在typescript中表示“任何函數”,為此,讓我們看一下 TypeScript 的一些內置類型Parameters
以及 ReturnType
。
export type Parameters<
T extends (...args: any) => any
> = T extends (...args: infer P) => any
? P
: never;
export type ReturnType<
T extends (...args: any) => any
> = T extends (...args: any) => infer R ? R : any;
您會注意到這兩種實用程序類型使用相同的約束:(...args: any) => any
。
(...args: any)
指定函數可以接受任意數量的參數,=> any
表示它可以返回任何內容。
5表示沒有參數的函數
要表達一個沒有參數的函數(但返回任何內容),您需要使用() => any
:
const wrapFuncWithNoArgs = (func: () => any) => {
try {
return func();
} catch (e) {}
};
wrapFuncWithNoArgs((a: string) => {});
> Argument of type '(a: string) => void' is not assignable to parameter of type '() => any'.
> Target signature provides too few arguments. Expected 1 or more, but got 0.
6總結
Function
不應該用作表示函數類型。當您只想指定參數而不指定返回類型時,可以使用語法(a: string, b: number) => any
。
記住,(...args: any) => any
可用于表示任何函數類型。
此處,mapper
表示從對象中提取數字的函數。該 sum 函數的強大之處在于您可以丟棄大多數此類聲明:
const youTubeVideos = [
{ name: "My favorite cheese", views: 100 },
{
name: "My second favorite cheese (you won't believe it)",
views: 67,
},
];
const result = sum(youTubeVideos, (video) => {
return video.views;
}); // 167
事實上,我們已經舍棄了所有類型聲明,但 video
仍舊被推斷為 { name: string; views: number }
。這是可能的,因為我們的函數定義的特殊性:(item: T) => number
。