如何确保元组元素标签得到保留



背景

我正在尝试使用标记的元组元素用rest参数替换现有的重载函数。

原始代码

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

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
}

问题

游乐场

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

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. 有更好/更简单的解决方案来实现我想要的结果吗

解决方案

游乐场

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

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]

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

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

根据文件:

它们纯粹是用于文档和工具

这只是我的猜测。

我认为标签被删除是因为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中创建了新的元组,因此元素的顺序可能不会被保留。因此,保留标签可能是不安全的。但这只是我的猜测。

在这里你可以找到PR


但是,您可以将标签添加到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]

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

最新更新