[英]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
不以任何方式將U
和I
聯系起來。 以下是類型檢查器可以從該簽名中推斷出的內容:
fnGeneric
是為所有類型V
和I
定義的函數,接收 2 個參數fn
和param
並返回類型V
fn
是為所有類型U
定義的函數,接收 1 個參數param
,類型U
並返回V
param
是I
型這種類型檢查很好,因為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.