如何包装函数并扩展具有正确类型的自定义参数

huangapple go评论63阅读模式
英文:

How to wrap function and extend custom arguments with right type

问题

I've translated the code part for you:

const func = (a: string) => {
   console.log(a);
}

type Options = {
  force?: boolean
};

const wrapFunc = <A extends any[], R>(fn: (...args: A) => R) => {
  const wrappedFunc = (...args: A & [options?: Options]) => {
     const options: Options = 'force' in args[args.length - 1] ? args.pop() : {};
     fn(...(args as A))
  }

  return wrappedFunc;
}

const newFunc = wrapFunc(func);

newFunc('a', {force: true})
// Argument of type '["a", { force: true; }]' is not assignable to parameter of type '[a: string] & [options?: Options | undefined]'.
//  Type '["a", { force: true; }]' is not assignable to type '[a: string]'.
//    Source has 2 element(s) but target allows only 1.(2345)

Please note that code translations may vary depending on the context and purpose.

英文:

I want wrap callback function and extend custom arguments, And i get error type alert

const func = (a: string) =&gt; {
   console.log(a);
}

type Options = {
  force?: boolean
};

const wrapFunc = &lt;A extends any[], R&gt;(fn: (...args: A) =&gt; R) =&gt; {
  const wrappedFunc = (...args: A &amp; [options?: Options]) =&gt; {
     const options: Options = &#39;force&#39; in args[args.length - 1] ? args.pop() : {};
     fn(...(args as A))
  }

  return wrappedFunc;
}

const newFunc = wrapFunc(func);

newFunc(&#39;a&#39;, {force: true})
// Argument of type &#39;[&quot;a&quot;, { force: true; }]&#39; is not assignable to parameter of type &#39;[a: string] &amp; // [options?: Options | undefined]&#39;.
//  Type &#39;[&quot;a&quot;, { force: true; }]&#39; is not assignable to type &#39;[a: string]&#39;.
//    Source has 2 element(s) but target allows only 1.(2345)

here is reproduced TypeScript playground

Could somebody help me make is right. Thanks!

答案1

得分: 0

The &amp; operator is not what you should use. number[] &amp; {prop: string} will expect an array with a prop property typed as a string, not another element. To push an element to a generic array parameter, you can do as follows: <A extends unknown[]>(args: [...A, Options]

This one isn't suitable for us since we want to have optional options, and it can be achieved by telling the compiler that we expect A or A with options:

args: [...A, Options] | A

Implementation:

const wrapFunc = <A extends any[], R>(fn: (...args: A) => R) => {
  const wrappedFunc = (...args: [...A, Options] | A) => {
    const options: Options = 'force' in args[args.length - 1] ? args.pop() : {};
    fn(...(args.slice(-1) as A));
  };

  return wrappedFunc;
};

// const newFunc: (...args: [a: string] | [string, Options]) => void
const newFunc = wrapFunc(func);

This works as expected; however, the label of a is removed in the hinting when you also accept Options. To fix it, we should use labeled tuples as follows:

const wrappedFunc = (...args: [...mainArgs: A, options: Options] | A) => {}

This way hinting will be better:

// const newFunc: (...args: [a: string] | [a: string, options: Options]) => void
const newFunc = wrapFunc(func);

playground

Note: You can also consider using the following approach, however, it may throw some Typescript errors depending on your tsconfig:

const wrappedFunc = (...args: [...mainArgs: A, options?: Options]) => {}

// const newFunc: (a: string, options?: Options) => voi
const newFunc = wrapFunc(func);
英文:

The &amp; operator is not what you should use. number[] &amp; {prop: string} will expect an array with a prop property typed as a string, not another element. To push an element to a generic array parameter, you can do as follows:
&lt;A extends unknown[]&gt;(args: [...A, Options]

This one isn't suitable for us since we want to have optional options, and it can be achieved by telling the compiler that we expect A or A with options:

args: [...A, Options] | A

Implementation:

const wrapFunc = &lt;A extends any[], R&gt;(fn: (...args: A) =&gt; R) =&gt; {
  const wrappedFunc = (...args: [...A, Options] | A) =&gt; {
    const options: Options = &#39;force&#39; in args[args.length - 1] ? args.pop() : {};
    fn(...(args.slice(-1) as A));
  };

  return wrappedFunc;
};

// const newFunc: (...args: [a: string] | [string, Options]) =&gt; void
const newFunc = wrapFunc(func);

This works as expected; however, the label of a is removed in the hinting when you also accept Options. To fix it, we should use labeled tuples as follows:

const wrappedFunc = (...args: [...mainArgs: A, options: Options] | A) =&gt; {}

This way hinting will be better:

// const newFunc: (...args: [a: string] | [a: string, options: Options]) =&gt; void
const newFunc = wrapFunc(func); 

playground

Note: You can also consider using the following approach, however, it may throw some Typescript errors depending on your tsconfig:

const wrappedFunc = (...args: [...mainArgs: A, options?: Options]) =&gt; {}

// const newFunc: (a: string, options?: Options) =&gt; voi
const newFunc = wrapFunc(func);

huangapple
  • 本文由 发表于 2023年5月7日 19:13:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76193571.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定