簡體   English   中英

避免從 Typescript 中的多個參數推斷泛型?

[英]Avoiding inferring generics from multiple arguments in Typescript?

考慮這個函數:

function fn<T>(a: T, b: T[]): T {}

我希望它從a推斷T ,然后檢查bT 相反,TS 從ab推斷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)

我認為這打破了一些關於類型何時自動擴展為numberstring以及何時不自動擴展的啟發式 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.

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