[英]Typescript function overload with rest parameters
我有这样的代码,有几个重载函数,参数为 rest:
type IAuthSelectors =
| 'selector1'
| 'selector2'
| 'selector3'
| 'selector4'
| 'selector5'
| 'selector6';
function getAuthState(selector: 'selector1'): string[];
function getAuthState(selector: 'selector2', param1: string): boolean;
function getAuthState(selector: 'selector3'): boolean;
function getAuthState(selector: 'selector4'): string;
function getAuthState(selector: 'selector5'): MyInterface;
function getAuthState(selector: 'selector6'): string;
function getAuthState(selector: IAuthSelectors, ...selectorArguments: unknown[]): unknown
function getAuthState(selector: IAuthSelectors, ...selectorArguments: unknown[]): unknown {
// return logic
}
function useAuthSelector(selector: 'selector1'): string[];
function useAuthSelector(selector: 'selector2', param1: string): boolean;
function useAuthSelector(selector: 'selector3'): boolean;
function useAuthSelector(selector: 'selector4'): string;
function useAuthSelector(selector: 'selector5'): MyInterface;
function useAuthSelector(selector: 'selector6'): string;
function useAuthSelector(selector: IAuthSelectors, ...selectorArguments: unknown[]): unknown {
const authState = getAuthState(selector, ...selectorArguments); // ERROR A spread argument must either have a tuple type or be passed to a rest parameter.ts(2556)
// ...
}
但是我不能让一个调用另一个。 错误: ERROR A spread argument must either have a tuple type or be passed to a rest parameter.ts(2556)
非常清楚。 但是,如果我尝试将它转换为一个元组,它就会崩溃。 我也尝试检查 rest 参数的数量来调用它是否带参数,但它不起作用。
目前,它只有一个带有 rest 个参数的“选择器”,但将来可能会有更多。 我添加了重载函数,以便在调用它们的组件中具有正确的类型。
有任何想法吗?
谢谢!
编辑:如果我添加传播超载,它不会失败,但我可以这样做:
type IAuthSelectors =
| 'selector1'
| 'selector2';
function getAuthState(selector: 'selector1'): string[];
function getAuthState(selector: 'selector2', param1: string): boolean;
function getAuthState(selector: IAuthSelectors, ...selectorArguments: unknown[]): unknown
function getAuthState(selector: IAuthSelectors, ...selectorArguments: unknown[]): unknown {
// return logic
}
getAuthState('selector1', 'param1', 'param2'); // NO ERROR
有什么方法可以保持参数的推断? 如果我添加传播重载,它不再推断其他原型。
我认为可能是 arguments 因为这两个函数是相同的。
在重载实现中类型有点……松散的情况并不少见。 :-) 您可能只是通过ts-expect-error
抑制错误(并且不公开调用的 rest arguments 版本),因为您知道 arguments 是正确的。 大多数时候我不喜欢这样做,但过载往往以这种方式达到 go(但请继续阅读)。
function useAuthSelector(selector: IAuthSelectors, ...selectorArguments: unknown[]): any {
// @ts-expect-error
const authState = getAuthState(selector, ...selectorArguments);
// ^? const authState: never
// ...
}
游乐场链接(旁注:在那里,我修复了重载实现函数的返回类型,它应该是重载签名中所有可能返回类型的any
一个或联合,而不是unknown
)
但是,除了使用ts-expect-error
的“ewww”因素外,还有一个 devex 问题,即authCheck
的类型never
。 :-|
或者,您可以为参数和返回类型使用映射类型而不是重载(但请继续阅读):
type IAuthSelectors =
| "selector1"
| "selector2"
| "selector3"
| "selector4"
| "selector5"
| "selector6";
type SelectorCall<Selector extends IAuthSelectors> =
Selector extends "selector1"
? {
params: [];
returnType: string[];
}
: Selector extends "selector2"
? {
params: [param1: string];
returnType: boolean;
}
: Selector extends "selector3"
? {
params: [];
returnType: boolean;
}
: Selector extends "selector4"
? {
params: [];
returnType: string;
}
: Selector extends "selector5"
? {
params: [];
returnType: MyInterface;
}
: Selector extends "selector6"
? {
params: [];
returnType: string;
}
: never;
function getAuthState<Selector extends IAuthSelectors, CallType extends SelectorCall<Selector>>(
selector: Selector,
...params: CallType["params"]
): CallType["returnType"] {
return null as any as CallType["returnType"];
}
function useAuthSelector<Selector extends IAuthSelectors, CallType extends SelectorCall<Selector>>(
selector: Selector,
...params: CallType["params"]
): CallType["returnType"] {
const authState = getAuthState(selector, ...params);
return authState;
}
const x = useAuthSelector("selector1");
// ^? const x: string[]
const y = useAuthSelector("selector2", "param value");
// ^? const y: boolean
您仍然可以获得参数建议和自动完成等。感谢命名元组元素,您甚至可以在执行param1
useAuthSelector("selector2", _
:
caTS 在评论中指出,像这样的映射条件相当慢(在编译时),而且这个可以很容易地用查找替换。 千真万确! 这样做意味着我们不必重复选择器名称,因为我们可以从该查找类型派生IAUthSelectors
,如下所示:
type SelectorCall = {
selector1: {
params: [];
returnType: string[];
};
selector2: {
params: [param1: string];
returnType: boolean;
};
selector3: {
params: [];
returnType: boolean;
};
selector4: {
params: [];
returnType: string;
};
selector5: {
params: [];
returnType: MyInterface;
};
selector6: {
params: [];
returnType: string;
};
};
type IAuthSelectors = keyof SelectorCall;
那么函数是:
function getAuthState<Selector extends IAuthSelectors, CallType extends SelectorCall[Selector]>(
selector: Selector,
...params: CallType["params"]
): CallType["returnType"] {
return null as any as CallType["returnType"];
}
function useAuthSelector<Selector extends IAuthSelectors, CallType extends SelectorCall[Selector]>(
selector: Selector,
...params: CallType["params"]
): CallType["returnType"] {
const authState = getAuthState(selector, ...params);
return authState;
}
用法是一样的,就像 IDE 的经验一样(带有参数名称等)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.