【问题标题】:How to reuse rest types in tuples?如何在元组中重用剩余类型?
【发布时间】:2023-03-04 10:54:02
【问题描述】:
function A<
    NumArr extends [] | number[],
    BoolArr extends [] | boolean[],
    Params extends [...NumArr, "middle-element", ...BoolArr] = [...NumArr, "middle-element", ...BoolArr]
>(...arr: Params): Params {
    return arr
}

//not a single error
const
    test1: [1, false] = A(1, false),
    test2: [false, "middle-element", 2] = A(false, "middle-element", 2),
    test3: [] = A()

如何让TS检测Params中元素的顺序是否符合要求? 或者只是解释为什么这段代码不符合直觉逻辑。

【问题讨论】:

    标签: typescript tuples


    【解决方案1】:

    这对于 TypeScript 来说并非易事。休息参数后的休息参数不会以您期望的方式工作。事实上,论点只是推断出来的,而不是经过验证的。不幸的是,您应该自己编写验证逻辑。

    直截了当。

    考虑这个例子:

    type Elem = number | boolean | "middle-element";
    
    type Last<T extends any[]> = T extends [infer _]
      ? never
      : T extends [...infer _, infer Tl]
      ? Tl
      : never
    
    type HandleEmpty<T extends any[], Data> = T['length'] extends 0 ? never : Data
    
    type Validation<Params extends any[], Cache extends Elem[] = []> =
      Params extends []
      ? Cache['length'] extends 0
      ? never
      : Cache
      : Params extends [infer Fst, ...infer Rest]
      ? Cache extends []
      ? Fst extends number
      ? HandleEmpty<Rest, Validation<Rest, [...Cache, Fst]>>
      : never
      : Fst extends number
      ? Last<Cache> extends number
      ? HandleEmpty<Rest, Validation<Rest, [...Cache, Fst]>>
      : never
      : Fst extends "middle-element"
      ? Last<Cache> extends number
      ? HandleEmpty<Rest, Validation<Rest, [...Cache, Fst]>>
      : never
      : "middle-element" extends Cache[number]
      ? Fst extends boolean
      ? Validation<Rest, [...Cache, Fst]>
      : never
      : never
      : never
    
    {
      type Test = Validation<[42, 43, "middle-element", false, true]> // ok
      type Test2 = Validation<[42, "middle-element", true, false]> // ok
    
      type Test3 = Validation<[false, "middle-element", 42]> // never
      type Test4 = Validation<[42, false]> // never
      type Test5 = Validation<[false, false, false, "middle-element", 42, "middle-element", 43]> //never
      type Test6 = Validation<[false, false, false, "middle-element", 42, false, 43]> // never
      type Test7 = Validation<[false, 42, "middle-element"]> // never
      type Test8 = Validation<[false, "middle-element", 42, false, "middle-element", 42,]> // never
      type Test9 = Validation<[false, "middle-element"]> // never
      type Test10 = Validation<[false]> // never
      type Test11 = Validation<[]> // never
      type Test12 = Validation<[42, "middle-element", boolean, "middle-element", 43]> //never
      type Test13 = Validation<[2, false, "middle-element"]> // never
    }
    
    type IsNever<T> = [T] extends [never] ? true : false;
    
    function check<
      Params extends Elem[],
      IsValid extends Validation<Params>
    >(...arr: IsNever<IsValid> extends true ? [never] : [...Params]) {
      return arr
    }
    
    /**
     * Ok
     */
    const valid = check(1, 'middle-element', false)
    const valid2 = check(1, 42, 43, 67, 'middle-element', false, true, false)
    
    /**
     * Errors
     */
    const test2 = check(false, "middle-element", 2)
    const test1 = check(1, false)
    const test3 = check()
    

    我了解Validation 实用程序类型根本不可读。

    这里有 js 表示。请把它当作一个解释。不知道这段代码在tuntime是否可以:

    const last = (data: any) => data[data.length - 1]
    
    const handleEmpty = (data: any[], defaultValue) => data.length === 0 ? null : defaultValue
    
    const never = null;
    
    const validation = (params: any[], cache: any[] = []): any[] | null => {
      if (params.length === 0 && cache.length === 0) {
        return never // never is null
      }
      const [fst, ...rest] = params;
      if (cache.length === 0) {
        if (typeof fst === 'number') {
          return handleEmpty(rest, validation(rest, [...cache, fst]))
        } else {
          return never
        }
      }
      if (typeof fst === 'number') {
        return handleEmpty(rest, validation(rest, [...cache, fst]))
      }
      if (typeof fst === "middle-element") {
        if (typeof last(cache) === 'number') {
          return handleEmpty(rest, validation(rest, [...cache, fst]))
    
        }
        return null
      }
      if (cache.includes('middle-element')) {
        if (typeof fst === 'boolean') {
          return handleEmpty(rest, validation(rest, [...cache, fst]))
        }
      }
      return never
    }
    

    您可能已经注意到,有很多 if 条件语句,但它非常严格且合乎逻辑。

    我还包括了几个Validation 实用程序类型的测试。你可以写更多。这取决于你。

    如果您想了解更多关于 typescript 验证的信息,请查看我的博客 herehere

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-13
      • 2019-08-02
      • 2019-01-13
      • 2020-12-16
      • 2019-05-31
      相关资源
      最近更新 更多