![](/img/trans.png)
[英]How to declare a specific type of empty array inside a object using typescript?
[英]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
。
任何帮助将非常感激。
为此,我们需要从列表中推断每个元素并检查p1
和p2
是否具有相同的类型。
要从列表中推断每个元素,我们需要使用可变元组类型。 除此之外,我们需要遍历每个元素并检查p1
和p2
。
看到这个:
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
只是检查p1
和p2
是否具有相同的类型。
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
根据输入参数推断类型。 它告诉您第二个对象不能有不匹配的类型,但它们可能与第一个对象类型相同( T1
和T2
可以合法地是相同的类型)。
您可以使泛型有条件,以便T2
不能扩展T1
但这可能会限制对象而不知道它们的形状。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.