打字稿根据先前元素的类型定义数组中元素的类型
typescript defining type of element in array based of the types of the previous elements
此类用例的示例是:
type OnlyExtendArr = ???;
const arr1: OnlyExtendArr =
[
{}, //ok
{name: 'name'}, //ok
{name:"name",age: 'age'}, //ok
{age: 'age'} // should error - 'name' is required so this item's type would extend previous type
];
在此示例中,我希望每个元素的类型都扩展前一个类型。
更实际地说,我想定义一个类型,其中索引 n
中的项目应该扩展索引 n-1
中的项目,但我不知道如何写它。
是否可以使用打字稿?
一个好的开始是提取数组中最后一个元素的类型,这似乎不是那么简单(因为你不能访问 typescript 中的 -1 元素)(这个 [=14= 的解决方案])
数组中的最后一个元素:
type LengthOfTuple<T extends any[]> = T extends { length: infer L } ? L : never;
type DropFirstInTuple<T extends any[]> = ((...args: T) => any) extends (arg: any, ...rest: infer U) => any ? U : T;
type LastInTuple<T extends any[]> = T[LengthOfTuple<DropFirstInTuple<T>>];
type Stuff = [number, boolean, '10'];
type last = LastInTuple<Stuff> //'10'
是的,在打字稿中是可能的。
type Last<T> = T extends [...infer _, infer L] ? L : never
type First<T> = T extends [infer Head, ...infer _] ? Head : never
type Tail<T> = T extends [infer _, ...infer Tail] ? Tail : never
type ReplaceFirst<T> = [[never], ...Tail<T>]
type Validator<T extends Array<any>, Result extends Array<any> = []> =
(T extends []
? Result
: (T extends [infer Head]
? (Head extends Last<Result> ? [...Result, Head]
: [...Result, never]
)
: (T extends [infer Head, ...infer Rest]
? (First<Rest> extends Head
? Validator<Rest, [...Result, Head]>
: Validator<ReplaceFirst<Rest>, [...Result, Head]>)
: never)
)
)
const builder = <
Prop extends PropertyKey,
Value extends string,
Elem extends Record<Prop, Value>,
Tuple extends Elem[]
>(tuple: [...Tuple] & Validator<[...Tuple]>) => tuple
const result = builder([
{}, //ok
{ name: 'name' }, //ok
{ name: "name", age: 'age' }, //ok
{ age: 'age' } // error
])
Elem
- 从 array/tuple
推断出的元素
Tuple
- 推断 array/tuple
Validator
- 遍历推断的 tuple/array 并检查下一个元素 (Rest[0] extends Head
) 是否扩展前一个元素。如果是,则使用此元素调用递归 Validator
,否则使用 never
而不是无效元素调用 Validator
。 'T extends [infer Head]' - 在最后一次调用之前,检查元素是否扩展了 Result
的最后一个元素。如果是 - 将 Element
推送到 Result
和 return Result
,否则将 never
推送到 Result
.
我已经使用 [...Tuple] & Validator<[...Tuple]>
将经过验证的元组与提供的参数合并。通过这种方式,TS 能够仅突出显示无效的论点而不是整个论点。
AFAIK,没有额外的功能是不可能的,因为你需要推断每个元素。
如果你想从数组中提取最后一个元素,你可以这样做:
type Last<T> = T extends [...infer _, infer L] ? L : never
type First<T> = T extends [infer Head, ...infer Tail] ? Head : never
请参阅 variadic tuple types 的文档。
如果你对元组操作感兴趣,可以查看我的article
P.S. This 在可变元组类型之前解决方案是可以的。
此类用例的示例是:
type OnlyExtendArr = ???;
const arr1: OnlyExtendArr =
[
{}, //ok
{name: 'name'}, //ok
{name:"name",age: 'age'}, //ok
{age: 'age'} // should error - 'name' is required so this item's type would extend previous type
];
在此示例中,我希望每个元素的类型都扩展前一个类型。
更实际地说,我想定义一个类型,其中索引 n
中的项目应该扩展索引 n-1
中的项目,但我不知道如何写它。
是否可以使用打字稿?
一个好的开始是提取数组中最后一个元素的类型,这似乎不是那么简单(因为你不能访问 typescript 中的 -1 元素)(这个 [=14= 的解决方案])
数组中的最后一个元素:
type LengthOfTuple<T extends any[]> = T extends { length: infer L } ? L : never;
type DropFirstInTuple<T extends any[]> = ((...args: T) => any) extends (arg: any, ...rest: infer U) => any ? U : T;
type LastInTuple<T extends any[]> = T[LengthOfTuple<DropFirstInTuple<T>>];
type Stuff = [number, boolean, '10'];
type last = LastInTuple<Stuff> //'10'
是的,在打字稿中是可能的。
type Last<T> = T extends [...infer _, infer L] ? L : never
type First<T> = T extends [infer Head, ...infer _] ? Head : never
type Tail<T> = T extends [infer _, ...infer Tail] ? Tail : never
type ReplaceFirst<T> = [[never], ...Tail<T>]
type Validator<T extends Array<any>, Result extends Array<any> = []> =
(T extends []
? Result
: (T extends [infer Head]
? (Head extends Last<Result> ? [...Result, Head]
: [...Result, never]
)
: (T extends [infer Head, ...infer Rest]
? (First<Rest> extends Head
? Validator<Rest, [...Result, Head]>
: Validator<ReplaceFirst<Rest>, [...Result, Head]>)
: never)
)
)
const builder = <
Prop extends PropertyKey,
Value extends string,
Elem extends Record<Prop, Value>,
Tuple extends Elem[]
>(tuple: [...Tuple] & Validator<[...Tuple]>) => tuple
const result = builder([
{}, //ok
{ name: 'name' }, //ok
{ name: "name", age: 'age' }, //ok
{ age: 'age' } // error
])
Elem
- 从 array/tuple
Tuple
- 推断 array/tuple
Validator
- 遍历推断的 tuple/array 并检查下一个元素 (Rest[0] extends Head
) 是否扩展前一个元素。如果是,则使用此元素调用递归 Validator
,否则使用 never
而不是无效元素调用 Validator
。 'T extends [infer Head]' - 在最后一次调用之前,检查元素是否扩展了 Result
的最后一个元素。如果是 - 将 Element
推送到 Result
和 return Result
,否则将 never
推送到 Result
.
我已经使用 [...Tuple] & Validator<[...Tuple]>
将经过验证的元组与提供的参数合并。通过这种方式,TS 能够仅突出显示无效的论点而不是整个论点。
AFAIK,没有额外的功能是不可能的,因为你需要推断每个元素。
如果你想从数组中提取最后一个元素,你可以这样做:
type Last<T> = T extends [...infer _, infer L] ? L : never
type First<T> = T extends [infer Head, ...infer Tail] ? Head : never
请参阅 variadic tuple types 的文档。
如果你对元组操作感兴趣,可以查看我的article
P.S. This 在可变元组类型之前解决方案是可以的。