函数中缺少参数作为参数不会引发编译器错误 [英] absent parameter from function as parameter does not throw a compiler error

查看:22
本文介绍了函数中缺少参数作为参数不会引发编译器错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是 TSC Version 2.4.2

注意这个界面:

interface CallbackWithNameParameter {cb:(名称:字符串)=>空白}

这个实现:

const aCallback: CallbackWithNameParameter = {cb: () =>{}};

是否不会像您预期的那样抛出编译错误.

请注意,这确实会引发错误:

const aSecondCallback: CallbackWithNameParameter = {cb: (num: number) =>{}};

这是 TSC 错误还是我误解了什么?aCallback 不应该导致编译错误吗?

解决方案

这不是错误.具有较少尾随参数的函数是可替代的具有更多尾随参数的函数(只要参数类型匹配).这显然是一个常见的问题,它明确 在 TypeScript 常见问题解答中提到.

大致的推理是:JavaScript 中的任何函数都可以使用任意数量的参数调用,并且让调用者向您的函数发送比您预期更多的参数总是安全的;你只是忽略它们.就你而言,

const aCallback: CallbackWithNameParameter = {cb: () =>{}};

是有效的,因为 aCallback.cb 忽略了第一个参数,它必须是一个 string:

aCallback.cb('嘿');//没问题

第二种情况 aSecondCallback 无效,因为它会错误地尝试将第一个参数解释为 number 而不是 string实际上会.这不安全,而且可能是一个错误.


差不多就是这样.希望有所帮助;祝你好运!


更新 1

@jbmilgrom

<块引用>

感谢您的回答.第一次阅读时完全有道理,但现在遇到了:aCallback.cb() is 编译错误,即使它的定义不是.这如何适应?

这是一个编译错误,因为你声明了 aCallback 的类型是 CallbackWithNameParameter,它的 cb 属性是一个需要 string 参数.TypeScript 不再知道或关心 aCallback 的值,其 cb 属性恰好不需要参数.

如果你已经这样做了:

interface CallbackWithNoParameter {cb: () =>空白}const aCallback: CallbackWithNoParameter = {cb: () =>{}};aCallback.cb();//好的

它会起作用.

替代性通常不是对称的:

declare let noParam: CallbackWithNoParameter;声明 let oneParam: CallbackWithNameParameter;oneParam = noParam;//是的noParam = oneParam;//不

您可以将窄类型(没有参数的回调)分配给更宽的类型(带一个参数的回调)而不会出错,但一般情况下不能反过来.


更新 2

@jbmilgrom

<块引用>

感谢更新 1!但是为什么打字稿会允许定义一些永远不能按定义调用的东西?在这种情况下,在实现 CallbackWithNameParameter 时,aCallback 可能会成功地定义为一个没有参数的空函数.然而,它不能被这样调用,因为你知道,它是 CallbackWithNameParameter 类型.换句话说(关于可替代性的对称性),即使最终引用(例如 oneParam)永远不能so在之后使用,为什么您可以将窄类型分配给更宽类型分配?

这是因为你通过声明更广泛的类型让 TypeScript 忘记了.您将牛奶(窄型,如 CallbackWithNoParameter)倒入标记为液体"的容器中.(更广泛的类型,如 CallbackWithNameParameter),然后尝试打开容器并将液体倒入您的茶中,而没有检查它是否不是漂白剂.你可能知道容器里有牛奶,但 TypeScript 只知道它是液体并试图保护你.避免这种情况的最佳方法是将牛奶倒入标有牛奶"字样的容器中.如果后来有人要你喝液体,你可以把牛奶容器里的东西给他们,但如果有人要你喝牛奶,你不能把液体容器里的东西给他们,除非有办法先检查是否是牛奶.

检查一个函数是否接受零参数而不在运行时调用它并查看它是否爆炸并不容易:

function isCallbackWithNoParameter(x: any): x is CallbackWithNoParameter {if (!('cb' in x)) 返回假;尝试 {x.cb()}赶上(e){返回假;}返回真;}声明 let noParam: CallbackWithNoParameter;声明 let oneParam: CallbackWithNameParameter;oneParam = noParam;//是的noParam = oneParam;//不oneParam.cb();//不如果(isCallbackWithNoParameter(oneParam)){noParam = oneParam;//好的,你先检查了oneParam.cb();//好的,你先检查了}

I'm using TSC Version 2.4.2

Note this interface:

interface CallbackWithNameParameter {
  cb: (name: string) => void
}

This implementation:

const aCallback: CallbackWithNameParameter = {
  cb: () => {}
};

does not throw a compile error as you might expect.

Note that this does throw an error:

const aSecondCallback: CallbackWithNameParameter = {
  cb: (num: number) => {}
};

Is this a TSC bug or am I misunderstanding something? Shouldn't aCallback cause a compile error?

解决方案

It's not a bug. A function with fewer trailing parameters is substitutable for a function with more trailing parameters (as long as the parameter types match). It's apparently such a common question that it is explicitly mentioned in the TypeScript FAQ.

The reasoning is roughly: any function in JavaScript may be called with any number of parameters, and it's always safe to have a caller send your function more parameters than you expect; you just ignore them. In your case,

const aCallback: CallbackWithNameParameter = {
  cb: () => {}
};

is valid because aCallback.cb ignores the first parameter, which must be a string:

aCallback.cb('hey'); // no problem

The second case, aSecondCallback, is invalid because it would incorrectly try to interpret the first parameter as a number instead of the string it will actually be. That's not safe and likely an error.


That's pretty much it. Hope that helps; good luck!


Update 1

@jbmilgrom said

Thanks for your answer. It made total sense on first read, but now running into this: aCallback.cb() is a compile error, even though its definition is not. How does this fit in?

It is a compile error beause you declared that the type of aCallback is CallbackWithNameParameter, whose cb property is a function requiring a string parameter. TypeScript no longer knows or cares about the value of aCallback, whose cb property happens not to need a parameter.

If you had done this instead:

interface CallbackWithNoParameter {
  cb: () => void
}

const aCallback: CallbackWithNoParameter = {
  cb: () => {}
};
aCallback.cb(); // okay

it would work.

Substitutability is not generally symmetric:

declare let noParam: CallbackWithNoParameter;
declare let oneParam: CallbackWithNameParameter;
oneParam = noParam; // yes
noParam = oneParam; // no

You can assign a narrow type (a callback with no parameters) to a wider type (a callback with one parameter) without error, but you cannot do the reverse in general.


Update 2

@jbmilgrom said

Thanks for Update 1! But why would typescript allow the definition of something that can never be called as defined? In this case, aCallback may successfully be defined as an empty function with no parameter when implementing CallbackWithNameParameter. Yet, it cannot be called as such, since ya know, it is of type CallbackWithNameParameter. In other words (pertaining to symmetry of substitutability), why can you assign a narrow type to a wider type even though the ultimate reference (e.g. oneParam) can never so used after assigned?

It's because you made TypeScript forget by declaring the wider type. You poured milk (narrow type, like CallbackWithNoParameter) into a container marked "liquid" (wider type, like CallbackWithNameParameter), and then tried to open the container and pour the liquid into your tea without checking that it wasn't, say, bleach. You may know that there's milk in the container, but TypeScript only knows that it's a liquid and tries to protect you. The best way to avoid this is to pour the milk into a container marked "milk". If someone asks you for liquid later you can give them the contents of a milk container, but if someone asks you for milk you can't give them the contents of a liquid container unless there's some way to check if it's milk first.

It isn't easy to check for whether a function accepts zero arguments without calling it at runtime and seeing if it blows up:

function isCallbackWithNoParameter(x: any): x is CallbackWithNoParameter {
  if (!('cb' in x)) return false;
  try { 
    x.cb()
  } catch (e) {
    return false;
  }
  return true;
}

declare let noParam: CallbackWithNoParameter;
declare let oneParam: CallbackWithNameParameter;

oneParam = noParam; // yes
noParam = oneParam; // no
oneParam.cb(); // no
if (isCallbackWithNoParameter(oneParam)) {
  noParam = oneParam; // okay, you checked first
  oneParam.cb(); // okay, you checked first
}

这篇关于函数中缺少参数作为参数不会引发编译器错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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