打字稿根据先前元素的类型定义数组中元素的类型

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
])

Playground

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 在可变元组类型之前解决方案是可以的。