【问题标题】:How can you ensure that tuple element labels are preserved?如何确保保留元组元素标签?
【发布时间】:2021-12-26 13:57:55
【问题描述】:

背景

我正在尝试使用带标签的元组元素将现有的重载函数替换为剩余参数。

原始代码

这是原始重载函数的简化版本:

function doSomething(arg1: string, arg2: number):void;
function doSomething(arg1: string, arg2: number, arg3: unknown[]):void;
function doSomething(arg1: string, arg2: number, arg3: boolean):void;
function doSomething(arg1: string, arg2: number, arg3: unknown[], arg4: boolean):void {
    // ...implementation
}

所以第三个和第四个参数是可选的,但是当提供时,顺序可以是:

arg3: unknown[]
arg3: unknown[], arg4: boolean
arg3: boolean

尝试的解决方案

我决定首先创建带标签的元组元素,然后根据提供的类型变量类型将它们的类型设置为提供的类型或never。然后过滤该元组,删除任何 never 元素并返回结果。

type CalcAdditionArgs<ThirdArgType extends unknown[], FourthArgType extends boolean> = [
    myArg3: ThirdArgType extends [] ? never : ThirdArgType,
    myArg4 : [FourthArgType] extends [boolean] ? FourthArgType extends true ? true : never : never
]

type GetAdditionalArgs<ThirdArgType extends unknown[], FourthArgType extends boolean> = 
    FilterType<CalcAdditionArgs<ThirdArgType, FourthArgType>,  never>

FilterType 是元组过滤实用程序的修改版本,可在此处找到 https://stackoverflow.com/a/64034671/1798234

type FilterType<T extends unknown[], U = undefined> = 
    (T extends [] ? 
        [] :
        (T extends [infer H, ...infer R] ?
            ([H] extends [U] ?
                FilterType<R, U> :
                [H, ...FilterType<R, U>]) : 
            T
        )
    );

为了清楚起见,这是使用它们的函数

function execute<
    ThirdArgType extends unknown[] = [],
    FourthArgType extends boolean = false
>(
    arg1: string,
    arg2: number,
    ...args:GetAdditionalArgs<ThirdArgType, FourthArgType>
    
): void {
    // do something here
}

问题

Playground

这是 2 种实用程序类型的输出:

type a = CalcAdditionArgs<[], false>; //  [myArg3: never, myArg4: never]
type b = CalcAdditionArgs<[], true>; // [myArg3: [string, number], myArg4: never]
type c = CalcAdditionArgs<[string, number], false>; // [myArg3: [string, number], myArg4: never]
type d = CalcAdditionArgs<[string, number, Function], true>; // [myArg3: [string, number, Function], myArg4: true]

type e = GetAdditionalArgs<[], false>; // []
type f = GetAdditionalArgs<[], true>; // [true]
type g = GetAdditionalArgs<[string, number], false>; // [string: number]
type h = GetAdditionalArgs<[string, number, Function], true>; // [[string, number, Function], true]

如您所见,GetAdditionalArgs(或者更确切地说是FilterType)正在剥离元组元素标签。

问题

我无法理解如何(如果可能的话)在实用程序类型中创建并操作元组类型。即创建一个空元组,然后向其中添加所需的类型。因此,我的解决方案是预先创建填充的元组,然后删除元素。

  1. 有谁知道为什么元组元素标签被FilterType 剥离,有没有办法使用现有的解决方案来解决这个问题?

或者

  1. 是否有更好/更简单的解决方案来实现我正在寻找的结果?

解决方案

Playground

感谢 @captain-yossarian 的回复和 @ford04 (https://stackoverflow.com/a/64194372/1798234) 的 SO 回答,我能够重新考虑使用休息参数的解决方案方法:

type Append<E extends [unknown], A extends unknown[]> = [...A, ...E]

type GetMyArrayArg<T extends unknown[]> = [myArrayArg: T extends [] ? never : T]
type GetMyBooleanArg<T extends boolean> = [myBooleanArg : [T] extends [boolean] ? T extends true ? true : never : never]


type AddParameter<T extends [unknown], U extends unknown[] = []> =
    T extends [] ? 
        U :
        T extends [infer H] ?
            [H] extends [never] ? 
                U : 
                Append<T, U> :
            U

type GetAdditionalArgs<ThirdArgType extends unknown[], FourthArgType extends boolean> = 
    AddParameter<
        GetMyBooleanArg<FourthArgType>, 
        AddParameter<
            GetMyArrayArg<ThirdArgType>>
        >


type a = GetAdditionalArgs<[], false>; // []
type b = GetAdditionalArgs<[], true>; // [myBooleanArg: true]
type c = GetAdditionalArgs<[string, number], false>; // [myArrayArg: [string, number]]
type d = GetAdditionalArgs<[string, number, Function], true>; // [myArrayArg: [string, number, Function], myBooleanArg: true]

【问题讨论】:

  • 能否请您将可重现的示例放入一段代码并评论您遇到的错误?我并不是说你的问题不清楚。只是会更容易挖掘它
  • 完全忘记添加了,谢谢提醒 :)

标签: typescript tuples typescript-generics


【解决方案1】:

如果您好奇为什么从输出中删除元组标签,可能很难回答。

首先,你可以在这里找到docs。

根据文档:

它们纯粹是为了文档和工具

这只是我的猜测。

我认为标签被删除是因为FilterType 实用程序类型。 FilterType 创建了全新的元组,似乎 TS 在迭代过程中不保留元组标签。

考虑这个例子:

type Labeled = [name: string];

type Infer<L extends Labeled> = L extends [infer First] ? [First] : never

type Result = Infer<Labeled> // [string], no label

数字索引也不保留标签。 [L[0]] - 返回[string]。

似乎是它与休息参数一起工作:

type Labeled = [name: string];

type Infer<L extends Labeled> = L extends [infer _] ? [...L] : never

type Result = Infer<Labeled> // [name: string], with label

CalcAdditionArgs 不创建新元组。

我在文档中没有找到如何保存标签。

我假设,由于您在 FilterType 中创建了新元组,因此可能不会保留元素的顺序。因此,保留标签可能不安全。但这只是我的猜测。

Here你可以找公关


但是,您可以将标签添加到FilterType:

type FilterType<T extends unknown[], U = undefined> =
    (T extends [] ?
        [] :
        (T extends [infer H, ...infer R] ?
            ([H] extends [U] ?
                FilterType<R, U> :
                [Batman: H, ...Superman: FilterType<R, U>]) : // <----- LABELS
            T
        )
    );

您可能已经注意到,我添加了Batman 标签和Superman 标签,它们存在于输出中:

type FilterType<T extends unknown[], U = undefined> =
    (T extends [] ?
        [] :
        (T extends [infer H, ...infer R] ?
            ([H] extends [U] ?
                FilterType<R, U> :
                [Batman: H, ...Superman: FilterType<R, U>]) :
            T
        )
    );


type CalcAdditionArgs<ThirdArgType extends unknown[], FourthArgType extends boolean> = [
    myArg3: ThirdArgType extends [] ? never : ThirdArgType,
    myArg4: [FourthArgType] extends [boolean] ? FourthArgType extends true ? true : never : never
]

type GetAdditionalArgs<ThirdArgType extends unknown[], FourthArgType extends boolean> =
    FilterType<CalcAdditionArgs<ThirdArgType, FourthArgType>, never>


type e = GetAdditionalArgs<[], false>; // []
type f = GetAdditionalArgs<[], true>; // [Batman: true]
type g = GetAdditionalArgs<[string, number], false>; // [Batman: [string, number]]
type h = GetAdditionalArgs<[string, number, Function], true>; // [Batman: [string, number, Function], Batman: true]

如果这不符合您的期望,您可以手动创建允许的元组的并集。这将是最安全的方式

【讨论】:

  • 非常感谢您的解释,这很有道理。提到休息参数工作让我想起了我在这里遇到的另一个解决方案stackoverflow.com/a/64194372/1798234,但当时想不出如何使用
  • @james 欢迎您。感谢链接
猜你喜欢
  • 1970-01-01
  • 2017-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-19
  • 1970-01-01
  • 1970-01-01
  • 2016-04-19
相关资源
最近更新 更多