簡體   English   中英

使用帶有打字稿的泛型的具有特定類型的對象數組

[英]An array of object with specific type using generics with typescript

我想要一個以下類型的對象數組(任意長度):

[
  {p1: T,  p2: T}, 
  {p1: T2, p2: T2}
  ...
]

對於單個對象,這可以按我的意願工作:

type O<T> = {
  p1: T;
  p2: T;
};


const f = <T,>(a: O<T>) => { 
  console.log(a) 
}

f({ p1: 1, p2: 2 }); // makes sure p1 and p2 has the same type

我想將對象數組傳遞給函數f ,並且我希望每個對象單獨具有相同的類型約束。

我試過了:

O<T> = {
  p1: T;
  p2: T;
}

const f = <T,>(a: O<T>[]) => { 
  console.log(a) 
}

f([
  { p1: 1, p2: 2 },
  { p1: '3', p2: '4' }, // Type 'string' is not assignable to type 'number'
]);

我也試過:

type O<T> = {
  p1: T;
  p2: T;
};

type A<T> = {
  [P in keyof T]: O<T[P]>;
};

const f = <T,>(a: A<T>) => { 
  console.log(a) 
}

f([
  { p1: 1, p2: 2 },
  { p1: 3, p2: '4' }, // allows but I don't want it to be allowed p1 != p2 unfortunatly T resolvs to string | number
]);

我希望O<T>分別應用於數組中的每個對象。 我嘗試了很多東西,在這一點上我不確定是否可以使用打字稿來實現這一點。

我希望這適用於任何自定義類型,而不僅僅是number | string number | string

任何幫助將非常感激。

為此,我們需要從列表中推斷每個元素並檢查p1p2是否具有相同的類型。

要從列表中推斷每個元素,我們需要使用可變元組類型 除此之外,我們需要遍歷每個元素並檢查p1p2

看到這個:

type BothEqual<T extends Base<any>> =
    T['p1'] extends T['p2']
    ? (T['p2'] extends T['p1']
        ? T
        : never)
    : never

type Validation<
    Arr extends Array<unknown>,
    Result extends Array<unknown> = []
    > =
    Arr extends []
    ? []
    : (Arr extends [infer H extends Base<any>]
        ? [...Result, BothEqual<H>]
        : (Arr extends [infer Head extends Base<any>, ...infer Tail]
            ? Validation<[...Tail], [...Result, BothEqual<Head>]>
            : Readonly<Result>)
    )

BothEqual只是檢查p1p2是否具有相同的類型。

Validation - 遍歷列表中的 eack 元素並在其上調用BothEqual 因此,如果 elem 有效,則返回此元素,否則返回never

interface Base<T> {
    p1: T,
    p2: T
};


type BothEqual<T extends Base<any>> =
    T['p1'] extends T['p2']
    ? (T['p2'] extends T['p1']
        ? T
        : never)
    : never


type Validation<
    Arr extends Array<unknown>,
    Result extends Array<unknown> = []
    > =
    /**
     * If Arr is empty tuple, return empty result
     */
    Arr extends []
    ? []
    /**
     * If Arr is a tuple with one element, stop recursion.
     * See TS 4.7 https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#extends-constraints-on-infer-type-variables docs
     * for [infer H extends Base<any>] syntax
     * 
     */
    : (Arr extends [infer H extends Base<any>]
        ? [...Result, BothEqual<H>]
        /**
         * If Arr has more than one element - call it recursively
         */
        : (Arr extends [infer Head extends Base<any>, ...infer Tail]
            ? Validation<[...Tail], [...Result, BothEqual<Head>]>
            : Readonly<Result>)
    )


function f<T, List extends Base<T>[]>(a: [...List] & Validation<[...List]>) {
    console.log(a)
}

f([
    { p1: 1, p2: 2 },
    { p1: 3, p2: '42' } // error
]);

Playground您可能已經注意到,提供的參數中的第二個元素被突出顯示。 此外,您可以將p1優先於p2 這意味着p1是真實的來源。 然后,如果p2無效,則僅突出顯示p2 嘗試這個:

type BothEqual<T extends Base<any>> =
    T['p2'] extends T['p1']
    ? T
    : Omit<T, 'p2'> & { p2: never }

您可以簡化Validation類型:

type Validation<
    Arr extends Array<unknown>,
    > =
    {
        [Elem in keyof Arr]: Arr[Elem] extends Base<any> ? BothEqual<Arr[Elem]> : never
    }

但是隨后數組中的所有元素都將突出顯示,這可能很難理解出了什么問題。

如果您對 TS 函數參數驗證和元組迭代感興趣,可以查看我博客中的這些文章:

我想你一開始就擁有它;-)

type O<T1, T2> = [ { p1: T1, p2: T1 }, { p3: T2, p4: T2 } ]

function f<T1,T2>(a: O<T1, T2>) { 
  console.log(a) 
}

f([
  { p1: 1, p2: 2 },
  { p3: '3', p4: '4' }
]);

f<number, string>([
  { p1: 1, p2: 2 },
  { p3: '3', p4: '4' }
]);

f<number, string>([
  { p1: 1, p2: 2 },
  { p3: 3, p4: '4' }
  //^^ Type 'number' is not assignable to type 'string'
]);

在沒有顯式類型的情況下對{p3: 3, p4: '4'}進行正確警告的部分問題是,泛型f根據輸入參數推斷類型。 它告訴您第二個對象不能有不匹配的類型,但它們可能與第一個對象類型相同( T1T2可以合法地是相同的類型)。

您可以使泛型有條件,以便T2不能擴展T1但這可能會限制對象而不知道它們的形狀。

暫無
暫無

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

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