![](/img/trans.png)
[英]TypeScript: Inferring function arguments from a constructor signature
[英]Avoiding inferring generics from multiple arguments in Typescript?
考慮這個函數:
function fn<T>(a: T, b: T[]): T {}
我希望它從a
推斷T
,然后檢查b
與T
。 相反,TS 從a
和b
推斷T
例如:
fn(1, [1, 'str']);
這將返回number | string
number | string
。 但是,我希望將T
推斷為number
,因此這會引發類似“number | string is not assignable to number”的錯誤。 這可能嗎?
您可以提供顯式類型:
declare function fnA<T>(a: T, b: (T extends T ? T : never)[]): T
fnA<number>(1, [2, 'str'])
// Type 'string' is not assignable to type 'number'.(2322)
或執行以下操作:
declare function fnB<T>(a: T, b: (T extends T ? T : never)[]): T
fnB(1, [1, 'str']);
// Type 'string' is not assignable to type '1'.(2322)
這迫使編譯器必須檢查a
的類型以找出b
的類型。
但這似乎推斷出比number
更具體的類型,所以這不起作用:
fnB(1, [1, 2]);
// Type '2' is not assignable to type '1'.(2322)
我認為這打破了一些關於類型何時自動擴展為number
或string
以及何時不自動擴展的啟發式 Typescript。 打字稿很難知道約束該類型的嚴格程度。
但是,如果第一個參數是更廣泛的類型,例如number
,它將正常工作:
const x: number = 1
fnB(x, [1, 2]); // fine
或者,如果您不喜歡它的推斷方式,您可以再次提供類型:
fnB<number>(1, [1, 2]); // fine
雖然這只是一個想法,但您也許可以使用 HOF
declare const fn = <T,>(a: T) => (b:T): T
fn(1)([1,'str'])
// Argument of type '(string | number)[]' is not assignable to parameter of type 'number'.ts(2345)
但是正如@alex 建議的那樣,只要您知道,最好提供顯式類型
有一種替代方法:
type Primitives =
| string
| number
| bigint
| boolean
| symbol
| null
| undefined
type BackwardInference<T, P=Primitives> =
P extends any ? T extends P ? P : never : never;
declare function fn<T>(a: T, b: BackwardInference<T>[]): T
fn(1, [45]) // ok
fn('str', ['hello']) // ok
fn(42, ['str']) // expected error
因為如果第一個參數是原始參數,TS 會推斷文字類型,我們可以稍微放松推斷嚴格性。 這意味着當 first 將是文字42
時,第二個參數將被期望為number
而不是42
一種選擇是讓您的函數在推斷它們時采用兩個通用參數。 通過這種方式,您可以輕松地將參數不是相互依賴的想法編碼,而是一個依賴於另一個。
主要的警告是,要同時使用文字和復雜類型,您需要擴展冗長的文字。
type WidenLiteral<T> =
T extends string ? string :
T extends number ? number :
T extends bigint ? bigint :
T extends boolean ? boolean :
T extends symbol ? symbol :
T;
function fn<T, U>(a: T, b: T extends U ? WidenLiteral<T>[] : never): T {
return a;
}
fn(1, [2,3,4]); // Valid
fn('foo', ['bar']); // Valid
fn(true, [false, true]); // Valid
fn({ foo: 42 }, [{ foo: 1 }, { foo: 2 }]); // Valid
fn(1, ['foo']); // Invalid
fn(false, [0]); // Invalid
fn({ foo: 42 }, [{ foo: "1" }, { foo: 2 }]); // Invalid
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.