繁体   English   中英

Typescript function 参数过载 rest

[英]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", _ :

当“selector2”用于选择器参数时,IDE 显示 param1 的参数名称

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM