泛型 - 在 TypeScript 中,有可能定義一個表示具有一個參數的函數的類型而不使用 'any' 嗎?
很明顯,使用 any 關鍵字這相當簡單:
type SingleParamFunction = (arg: any) => any;
const foo: SingleParamFunction = (x: number): number => x + 1;
我已經嘗試了以下方法:
type SingleParamFunction = (arg: unknown) => unknown;
const foo: SingleParamFunction = (x: number): number => x + 1;
然而,編譯器抱怨 'unknown' 無法指派給類型 'number'。
我很好奇是否有可能在不使用 any 的情況下做到,也許可以使用泛型的解決方案。
一個潛在的使用案例可能看起來像如下:
interface Foo<I, O> {
input: I;
transform: (x: I) => O;
}
const foos: Foo<any, any>[] = [
{
input: 1,
transform: (x: number) => x + 1,
},
{
input: 'hello',
transform: (s: string) => s + ' world',
},
];
for (const foo of foos) {
console.log(foo.transform(foo.input));
}
答案#1
對你的問題有幾種解決方案,它們同樣有效,但逐步增強了類型安全性。
第一個是你提出的瑣碎例子:(x: any) => any。儘管有「絕不要使用 any」的經驗法則,但有判斷性的使用 any 仍然可以是一個有效的選擇。如果你唯一需要做的就是告訴編譯器這個函數接受一個 任意類型 的參數,那為什麼不呢?
第二個更合理,已經在評論中被 fjc 提出:
為什麼不使用像這樣的泛型:type SingleParamFunction=(arg: T) => T; const foo: SingleParamFunction=(x) => x + 1;?
這解決了反變的問題,但編譯器會對 transform 方法的簽名提出警告,因為聯集類型將被傳遞,因此 x 和返回類型將被推斷為聯集類型(例如,string | number):
interface Foo<I> {
input: I;
transform: SingleParamFunction<I>;
}
const foos: Foo<string | number>[] = [
{
input: 1,
transform: (x) => x + 1, //Type 'string' is not assignable to type 'number'.(2322)
},
{
input: 'hello',
transform: (s: string) => s + ' world', //Type 'string | number' is not assignable to type 'string'.
},
];
for (const foo of foos) {
console.log(foo.transform(foo.input));
}
然而,這並不是解決方案的問題。因為 foos 被定義為可變陣列,編譯器並不知道在給定成員中 input 或 transform 的 x 和返回類型是什麼,只知道它應該是 string | number。
如果你移除類型註解,你會看到,然而,陣列的類型已被正確推斷。這應該會給你提示需要檢查的內容,並引導我們到第三種解決方案:
({
input: number;
transform: (x: number) => number;
} | {
input: string;
transform: (s: string) => string;
})[]
那麼,你會怎麼做呢?映射類型來幫忙!你需要定義一個「Foo 的陣列,其中對於每個可能的 input 類型,可能有一個成員是這個類型的 Foo」。下面是這種類型的可能寫法(使用鍵重映射的幫助):
type validator<I> = Array<{ [T in I as number]: Foo<T> }[number]>;
就這樣:
//all ok, valid assignment
const bars: validator<string | number> = [
{
input: 1,
transform: (x: number) => x + 1
},
{
input: 'hello',
transform: (s: string) => s + ' world'
},
];
const invalid : validator<string|number> = [
{
input: "nope", //Type 'string' is not assignable to type 'number'.(2322)
transform: (x: number) => x + 1
},
{
input: 'hello',
transform: (s: string) => Infinity //Type 'number' is not assignable to type 'string'.(2322)
},
];
遊樂場
