简体   繁体   English

Typescript function 参数过载 rest

[英]Typescript function overload with rest parameters

I have a code like this, with a couple of overloaded functions 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)

      // ...
    }

But I cannot make one to call the other.但是我不能让一个调用另一个。 The error: ERROR A spread argument must either have a tuple type or be passed to a rest parameter.ts(2556) is pretty clear.错误: ERROR A spread argument must either have a tuple type or be passed to a rest parameter.ts(2556)非常清楚。 But if I try to cast it to a tuple it breaks.但是,如果我尝试将它转换为一个元组,它就会崩溃。 I have also tried checking the number of rest parameters to call it with parameters or not, but it doesn't work.我也尝试检查 rest 参数的数量来调用它是否带参数,但它不起作用。

Right now, this has only one "selector" with rest parameters, but there could be more in the future.目前,它只有一个带有 rest 个参数的“选择器”,但将来可能会有更多。 I added the overload functions to have proper types in the components calling them.我添加了重载函数,以便在调用它们的组件中具有正确的类型。

Any ideas?有任何想法吗?

Thank you!谢谢!


EDIT: If I add the spread overload, it doesn't fail, but I can do something like this:编辑:如果我添加传播超载,它不会失败,但我可以这样做:

    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

Any way to keep the inference of parameters?有什么方法可以保持参数的推断? If I add the spread overload it no longer infers the other prototypes.如果我添加传播重载,它不再推断其他原型。

I take it the possible arguments for the two functions are identical.我认为可能是 arguments 因为这两个函数是相同的。

It's not unusual inside an overload implementation for types to be a bit... loose.在重载实现中类型有点……松散的情况并不少见。 :-) You might just suppress the error via ts-expect-error (and not expose the rest arguments versions of the calls), since you know the arguments are correct. :-) 您可能只是通过ts-expect-error抑制错误(并且不公开调用的 rest arguments 版本),因为您知道 arguments 是正确的。 I'm not a fan of doing that most of the time, but overloads tend to go this way (but keep reading).大多数时候我不喜欢这样做,但过载往往以这种方式达到 go(但请继续阅读)。

function useAuthSelector(selector: IAuthSelectors, ...selectorArguments: unknown[]): any {
    // @ts-expect-error
    const authState = getAuthState(selector, ...selectorArguments);
    //    ^? const authState: never

    // ...
}

Playground link (side note: In there, I fixed the return type of the overload implementation functions, which should be any or a union of all the possible return types from the overload signatures, not unknown ) 游乐场链接(旁注:在那里,我修复了重载实现函数的返回类型,它应该是重载签名中所有可能返回类型的any一个或联合,而不是unknown

But , aside from the "ewww" factor of using ts-expect-error , there's also the devex issue that authCheck 's type would be never .但是,除了使用ts-expect-error的“ewww”因素外,还有一个 devex 问题,即authCheck的类型never :-| :-|

Alternatively, you could use a mapped type for the parameters and return type instead of overloads (but keep reading):或者,您可以为参数和返回类型使用映射类型而不是重载(但请继续阅读):

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

Playground link 游乐场链接

You still get argument suggestions and auto-complete, etc. Thanks to named tuple elements , you even get the name param1 showing when you do useAuthSelector("selector2", _ :您仍然可以获得参数建议和自动完成等。感谢命名元组元素,您甚至可以在执行param1 useAuthSelector("selector2", _ :

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

caTS points out in a comment that mapped conditionals like that are fairly slow (at compile time) and this one could easily be replaced with a lookup. caTS 在评论中指出,像这样的映射条件相当慢(在编译时),而且这个可以很容易地用查找替换。 Quite true!千真万确! And doing so means we don't have to repeat the selector names, because we can derive IAUthSelectors from that lookup type, like this:这样做意味着我们不必重复选择器名称,因为我们可以从该查找类型派生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;

Then the functions are:那么函数是:

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;
}

Playground link 游乐场链接

Usage is the same, as is the IDE experience (with parameter names and such).用法是一样的,就像 IDE 的经验一样(带有参数名称等)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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