網站搜尋

泛型 - 在 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)
    }, 
];

遊樂場