我想在保留所有函数参数名称的同时替换函数中最后一个参数的类型。就我而言,最后一个参数也是可选的。
例如:
type OrigArg = { arg: number };
type ReplacedArg = { arg: string };
type OrigFunc = (a: number, b: string, c?: OrigArg) => string
type ReplaceLast<TFunc, TReplace> = // type I'm looking for
type ReplacedFunc = ReplaceLast<OrigFunc, ReplacedArg>
// type ReplacedFunc = (a: number, b: string, c?: ReplacedArg) => string
更复杂的是,前面的参数的数量和类型是可变的,我只知道最后一个参数将是某种类型,我想用自定义参数替换它。
答案 0 :(得分:3)
好的,这有点困难,我将对其进行解释。进入 TL; DR 。
打字稿中的参数名称仅在散布原始参数时保留,例如:
type OrigParams = Parameters<OrigFunc>;
type AnotherFunc = (...args: OrigParams) => any;
这是由于TS的性质,Parameters
元组实际上是一个特殊的元组,它使名称成为内部的,不可访问的元数据,这些元数据在更改元组或仅创建另一个元组时会被破坏。即:
type F = (a: any, b: any) => void;
type P1 = Parameters<F>;
type P2 = [any, any];
此处P1
和P2
是不同的类型,即使它们在结构上相同。
引用TS:
请注意,当从参数序列推断出元组类型并随后将其扩展到参数列表时(如U的情况),原始参数名称将在扩展中使用(但是,名称没有语义含义,否则无法观察到。)
但是,这可以通过使用参数元组上的映射类型来完成,因为映射元组保留参数名,并在出现“最后一个”项目问题时进行一些后期处理。
type ReplaceLastParam<TParams extends readonly any[], TReplace> = {
[K in keyof TParams]: // We should put the replace code here
}
K
是string
,即在具有三个元素的元组的情况下,keyof TParams
是"0" | "1" | "2"
。因此,在只有三个参数的情况下,我们可以轻松编写:type ReplaceParam2<TParams extends readonly any[], TReplace> = {
[K in keyof TParams]: K extends "2" ? TReplace : TParams[K]
}
type LastIndex<T extends readonly any[]> =
((...t: T) => void) extends ((x: any, ...r: infer R) => void) ? Exclude<keyof T, keyof R> : never;
此类型计算元组R
的键,该键的元组T
少一个元素,并将它们与Exclude
进行比较:因此Exclude<"0" | 1" | "2", "0" | "1">
就是{{1 }},我们的最后一项。请注意,我们在这里使用类型推断,而函数将进行其他推断。
"2"
),以包装刚刚更改的参数更改类型:TL; DR :
ReplaceLast
这很有趣!希望这能回答您的问题,但是,如果您确实需要在应用程序中使用它,那么我希望您知道自己在做什么,因为可以用更简单的方式解决该要求。