簡體   English   中英

嵌套函數的打字稿泛型推斷

[英]Typescript generic infer of nested function

我有這些 ts 功能:

const fnGeneric = <V,I>(fn:<U>(param:U) => V, param:I) => fn(param);
const fn = (some:string) =>  some;
const result = fnGeneric(fn,5);

但結果以靜態類型錯誤結束:

'(some: string) => string' 類型的參數不能分配給 '(param: U) => string' 類型的參數。 參數“some”和“param”的類型不兼容。 類型 'U' 不能分配給類型 'string

這種模式有什么問題? 我想你應該推斷我輸入的是數字,但我這里有一些空格。

我認為您的定義已關閉,不會自動推斷類型以符合您的意圖。 這是我將使用的定義:

const fnGeneric = <V,U>(fn: (param:U) => V, param: U) => fn(param);
const fn = (some: string) =>  some;
const result = fnGeneric(fn, 5);

這在函數和第二個參數中使用相同的類型參數U ,確保它們的兼容性。 當然調用將是無效的,因為fn只接受字符串作為參數。

如果您查看fnGeneric的類型簽名,就像它出現在.d.ts文件中一樣,其原因就會變得更加清楚。

declare const fnGeneric: <V,I>(fn:<U>(param:U) => V, param:I) => V

不以任何方式將UI聯系起來。 以下是類型檢查器可以從該簽名中推斷出的內容:

  1. fnGeneric為所有類型VI定義的函數,接收 2 個參數fnparam並返回類型V
  2. fn為所有類型U定義的函數接收 1 個參數param ,類型U並返回V
  3. paramI

這種類型檢查很好,因為fn是一個可以將任何類型轉換為V的函數,因此在定義fn(param)傳遞一個I是完全可以接受的。

當您嘗試調用此函數時,問題就開始了。 為了調用fnGeneric ,你需要給出一個可以接受任何類型的函數,並將它變成你想要從fnGeneric取出的fnGeneric 這是不可能的! 傳入一個const fn: (some: string) => string就像在你的例子中一樣不起作用,因為fn不接受任何類型,它只接受字符串。 簽名需要是const fn: <U>(some: U) => string 因此錯誤:

'(some: string) => string' 類型的參數不能分配給 '(param: U) => string' 類型的參數。 參數“some”和“param”的類型不兼容。 類型 'U' 不能分配給類型 'string

此外,根據您的示例fnGeneric(fn, 5) ,您試圖通過將fn強制為采用任何類型參數的函數,將number傳遞給采用string的函數。 這是一個類型檢查的代碼片段:

const fnGeneric = <A>(fn: <B>(param: B) => B, param: A): A => fn(param);
const fn = <A>(some: A): A => some;
const result: number = fnGeneric(fn, 5); // result = 5

這個例子中的fn通常被稱為identity函數——它返回給定的參數,它是具有簽名<A>(a: A) => A的函數的唯一有效實現(沒有任意類型轉換) . 通過擴展, fnGeneric是一個函數,它接受恆等函數和任何類型的參數,並將恆等函數的應用返回給該參數。

這種模式有什么問題?

定義的fnGeneric沒有任何意義,編寫滿足簽名的類型檢查的程序是不可能的。 使類型工作的唯一方法只是使它變得多余。 沒有任何情況下最好調用fnGeneric(identity, x)而不是identity(x) (或者就此而言,只是表達式x )。 它們都是完全等效的。

我可以給你更真實的場景,我制作了更簡單的 fn 版本,有真實的:

export const inject = <I,V>(fn:<U>(input?:U) => V, resolveWithPayload: boolean, resolveArgs?: I) => <R>(payload:R):R => { resolveWithPayload ? fn(payload) : resolveArgs ? fn(resolveArgs) : fn(); return payload; };

const fn = (value:number):number => {
    propertyToMutate = value;
    return propertyToMutate;
}

const res =_fish.inject(fn,false,60)(50);

但調用它以:

“(value: number) => number”類型的參數不能分配給“(input?: U) => number”類型的參數。 參數“值”和“輸入”的類型不兼容。 “U”類型不能分配給“數字”類型。

如果我按照你的方式改進代碼:

export const inject = <I,V,U>(fn:(input?:U) => V, resolveWithPayload: boolean, resolveArgs?: I) => <R>(payload:R):R => { resolveWithPayload ? fn(payload) : resolveArgs ? fn(resolveArgs) : fn(); return payload; };

它以注入自身的類型定義錯誤結束,例如:

TS2345:“R”類型的參數不能分配給“U”類型的參數。

TS2345:“I”類型的參數不能分配給“U”類型的參數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM