繁体   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