打字稿,需要两个函数签名之一 [英] Typescript, require either of two function signatures

查看:22
本文介绍了打字稿,需要两个函数签名之一的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用一个库,它给我一个回调,它期望 要么 一个错误作为它的第一个参数,或者 null 作为一个错误和一个值作为它的第二个参数.

I'm working with a library that gives me a a callback, which expects either an error as it's first argument, or null as an error and a value as it's second argument.

我正在尝试使用类型对其进行编码,因此打字稿将验证我是否没有同时传入错误和值,或者两者都不传入.

I'm trying to encode this with a type, so typescript will validate that I'm for example not passing in both a error and a value, or neither of them.

type Callback = {
  (error: undefined, value: string): void;
  (error: Error): void;
}

function doThings(c: Callback) {
    // Valid, no error, and a useful value.
    c(undefined, '1');
    // Valid, something went wrong, so we pass in an error, but no value.
    c(new Error());

    // Invalid. Since there's no value, there must be an error.
    c(undefined);
    // Invalid, there's both an error and a value.
    c(new Error(), '1');
}

function c1(error: undefined, value: string) { }

function c2(error: Error) { }

doThings(c1)
doThings(c2)

推荐答案

据我所知,您对 Callback 的定义正是您想要的.Callback 是一个可以使用参数(error: undefined, value: string) 调用的函数.它也是一个可以用参数(error: Error)调用的函数.一个有效的Callback必须支持这两种方式的调用.

As far as I can tell, your definition of Callback is exactly as you want it to be. Callback is a function that can be called with arguments (error: undefined, value: string). It is also a function that can be called with arguments (error: Error). A valid Callback must support being called in both of those ways.

重申一下,这很好:

type Callback = {
  (error: undefined, value: string): void;
  (error: Error): void;
}

function doThings(c: Callback) {
    c(undefined, '1');  // okay
    c(new Error()); // okay
}

而且 TypeScript 编译器在那里没有报告任何错误,这很好.

And the TypeScript compiler doesn't report any errors there, which is good.

似乎有问题的部分是您然后继续定义两个函数,其中一个不符合Callback规范.让我们来看看它们.第一:

The part that seems to be a problem is that you then go ahead and define two functions, neither of which conforms to the Callback specification. Let's look at them. First:

function c1(error: undefined, value: string) { 
   // some impl which expects value to be a string, e.g.,
   value.charAt(0);
};
doThings(c1); // error, as expected

这个函数需要两个参数.您不能使用参数 (error: Error) 调用它.所以调用 doThings(c1) 是编译器的错误,这正是您想要的.编译器错误告诉您 c1 不是 Callback.如果您通过类型断言或其他技巧强制编译器允许它,那么在编译器时一切都会好起来的,然后在运行时 doThings(c1) 将最终调用 c1(new Error()),假设 c1 用它的 value 参数做类似字符串的事情(如我上面所示),最终会抛出一个类似 错误:未定义没有属性.

This function requires two arguments. You cannot call it with arguments (error: Error). So the call doThings(c1) is an error by the compiler, which is exactly what you want. The compiler error tells you that c1 is not a Callback. If you force the compiler to allow it with type assertions or other trickery, everything will be fine at compiler time, and then at runtime doThings(c1) will end up calling c1(new Error()), which, assuming that c1 does string-like stuff with its value argument (as I showed above), ends up throwing an error like Error: undefined has no properties.

同样:

function c2(error: Error) { 
  // some impl which expects error to be an Error, e.g.,
    console.log(error.message);
 };
 doThings(c2); // error, as expected

这个函数要求第一个参数是一个Error.您不能使用参数 (error: undefined, value: string) 调用它.所以调用 doThings(c2) 是编译器的错误,这正是您想要的.编译器错误告诉您 c2 不是 Callback.如果您强制编译器使用类型断言或其他技巧来允许它,那么在编译器时一切都会好起来的,然后在运行时 doThings(c2) 将最终调用 c2(undefined, '1'),假设 c2 用它的 error 参数做类似错误的事情(如我上面所示),最终会抛出一个类似 <代码>错误:未定义没有属性.

This function requires that the first argument be an Error. You cannot call it with arguments (error: undefined, value: string). So the call doThings(c2) is an error by the compiler, which is exactly what you want. The compiler error tells you that c2 is not a Callback. If you force the compiler to allow it with type assertions or other trickery, everything will be fine at compiler time, and then at runtime doThings(c2) will end up calling c2(undefined, '1'), which, assuming that c2 does Error-like stuff with its error argument (as I showed above), ends up throwing an error like Error: undefined has no properties.

所以 c1c2 都不是有效的 Callback 对象.如果你想做一个有效的Callback,你可以做到.一种方法是创建一个更具体的函数类型,如下所示:

So neither c1 nor c2 are valid Callback objects. If you want to make a valid Callback, you can do it. One way is to make a more specific function type, like so:

function cSubtype(error: undefined | Error, value?: string) {
  if ((typeof error === 'undefined') && (typeof value === 'string')) {
    c1(error, value);
  } else if ((typeof error !== 'undefined') && (typeof value === 'undefined')) {
    c2(error);
  } else console.log('I got some other arguments');
}
doThings(cSubtype); // okay

cSubtype 函数比 Callback 更具体,因为它接受更通用的参数.(函数参数因逆变而异,这意味着您制作的越通用/越广泛函数参数,函数类型越具体/越窄.)它接受像 (error: undefined, value: string)(error: Error) 这样的参数.它还接受 (error: Error, value: string)(error: undefined).但是你仍然可以将 cWider 传递给 doThings,因为 cWiderCallback子类型>,就像一个 {a: string, b: boolean} 可以传递给期望 {a: string} 的东西.

The cSubtype function is more specific than a Callback, in that it accepts more general parameters. (Function parameters vary contravariantly, meaning that the more general/wider you make function parameters, the more specific/narrower you make the function type.) It accepts arguments like (error: undefined, value: string) as well as (error: Error). It also accepts (error: Error, value: string) and (error: undefined). But you can still pass cWider to doThings, because cWider is a subtype of Callback, just like a {a: string, b: boolean} can be passed to something expecting a {a: string}.

另一种方法是定义一个重载 函数的行为与您希望 Callback 的行为完全相同.它仍然需要一个更通用的实现签名,但不能用实现签名调用它(阅读有关重载的链接以获取更多信息).举个例子:

Another way you can do this is just to define an overloaded function which behaves exactly as you want a Callback to behave. It still needs an implementation signature which is more general, but it cannot be called with the implementation signature (read the link about overloads for more info). Here's an example:

function cOverload(error: undefined, value: string): void; // signature1
function cOverload(error: Error): void; // signature2
function cOverload(error: undefined | Error, value?: string) { //impl 
  if (typeof value !== 'undefined') {
    c1(undefined, value);
  } else {
    c2(error!);
  }
}
doThings(cOverload);  // works

<小时>

有意义吗?希望能帮助到你.祝你好运.


Does that make sense? Hope it helps. Good luck.

这篇关于打字稿,需要两个函数签名之一的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆