【问题标题】:typescript defining type of element in array based of the types of the previous elements打字稿根据先前元素的类型定义数组中元素的类型
【发布时间】:2026-01-08 00:45:02
【问题描述】:

这种用例的一个例子是:

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 元素)(这个here的解决方案)

数组中的最后一个元素:

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'

【问题讨论】:

    标签: typescript tuples type-inference variadic-tuple-types


    【解决方案1】:

    是的,在打字稿中是可能的。

    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 - 从数组/元组中推断出的元素

    Tuple - 推断数组/元组

    Validator - 遍历推断的元组/数组并检查下一个元素 (Rest[0] extends Head) 是否扩展了前一个元素。如果是,则使用此元素调用递归Validator,否则使用never 而不是无效元素调用Validator'T extends [infer Head]' - 在最后一次调用之前,检查元素是否从Result 扩展了最后一个元素。如果是 - 将Element 推送到Result 并返回Result,否则将never 推送到Result

    我使用[...Tuple] &amp; Validator&lt;[...Tuple]&gt; 将经过验证的元组与作为参数提供的合并。通过这种方式,TS 能够仅突出显示 invelid 参数而不是整个参数。

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

    【讨论】: