简体   繁体   English

如何在 TypeScript 中实现类型化的“两者”function?

[英]How can I implement a typed “both” function in TypeScript?

The storybook actions addon provides a convenient way to log callback invocations:故事书操作插件提供了一种记录回调调用的便捷方式:

renderButton({onClick: action('onClick')});

The action call returns a function that logs the string ( onClick ) and the arguments with which it's invoked. action调用返回一个 function 记录字符串 ( onClick ) 和调用它的 arguments 。

Sometimes I want to have both the action and something else:有时我想同时拥有action和其他东西:

renderButton({
  onClick: (arg1, arg2, arg3) => {
    action('onClick')(arg1, arg2, arg3);
    // ... my code
  }
});

The action call has gotten more verbose since I have to pass in all the arguments.由于我必须传入所有 arguments,因此action调用变得更加冗长。 So I've implemented a both function to let me write:所以我实现了一个both让我写:

renderButton({
  onClick: both(action('onClick'), (arg1, arg2, arg3) => {
    // ... my code
  })
});

This works great until I try to add TypeScript types.这很好用,直到我尝试添加 TypeScript 类型。 Here's my implementation:这是我的实现:

function both<T extends unknown[], This extends unknown>(
  a: (this: This, ...args: T) => void,
  b: (this: This, ...args: T) => void,
): (this: This, ...args: T) => void {
  return function(this: This, ...args: T) {
    a.apply(this, args);
    b.apply(this, args);
  };
}

This type checks and works at runtime, but depending on the context, it either results in unwanted any types or type errors.此类型在运行时检查并工作,但根据上下文,它会导致不需要的any类型或类型错误。 For example:例如:

const fnWithCallback = (cb: (a: string, b: number) => void) => {};

fnWithCallback((a, b) => {
  a;  // type is string
  b;  // type is number
});

fnWithCallback(
  both(action('callback'), (a, b) => {
    a;  // type is any
    b;  // type is any
  }),
);

fnWithCallback(
  both(action('callback'), (a, b) => {
//                         ~~~~~~~~~~~
// Argument of type '(a: T[0], b: T[1]) => number' is not assignable to
// parameter of type '() => void'.
  }),
);

Is it possible to have both correctly capture argument types from the callback context?是否可以both从回调上下文中正确捕获参数类型? And to avoid the any types that presumably come arise from the action declaration :为了避免可能来自action声明any类型:

export type HandlerFunction = (...args: any[]) => void;

Here's a playground link with the full example.这是带有完整示例的游乐场链接

This should work and keep correct callback argument types:这应该有效并保持正确的回调参数类型:

type UniversalCallback<Args extends any[]> = (...args: Args) => void;
function both<
    Args extends any[],
    CB1 extends UniversalCallback<Args>,
    CB2 extends UniversalCallback<Args>
>(fn1: CB1, fn2: CB2): UniversalCallback<Args> {
  return (...args:Args) => {
    fn1(...args);
    fn2(...args);
  };
}

This solution ignores this but I don't know if it's a problem for you because the examples of usage you gave didn't really use this .该解决方案忽略了this ,但我不知道这对您来说是否有问题,因为您提供的使用示例并没有真正使用this

It's easy enough to extend support to passing this to callbacks:扩展支持以将this传递给回调很容易:

type UniversalCallback<T, Args extends any[]> = (this:T, ...args: Args) => void;
function both<
    T,
    Args extends any[],
    CB1 extends UniversalCallback<T, Args>,
    CB2 extends UniversalCallback<T, Args>
>(fn1: CB1, fn2: CB2): UniversalCallback<T, Args> {
  return function(this:T, ...args:Args) {
    fn1.apply(this, args);
    fn2.apply(this, args);
  };
}

It works perfectly in the following test:它在以下测试中完美运行:

class A { f() {} }

const fnWithCallback = (cb: (this: A, a: string, b: number) => void) => { };

fnWithCallback(function(a, b) {
  a;  // type is string
  b;  // type is number
  this.f(); // works
});

fnWithCallback(
  both(function (a) {
    a; // correct type
    this.f(); // works
  }, function (a, b) {
    a; b; // correct types
    this.f(); // works
  }),
);

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

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