代码之家  ›  专栏  ›  技术社区  ›  jbmilgrom

函数中缺少参数作为参数不会引发编译器错误

  •  1
  • jbmilgrom  · 技术社区  · 7 年前

    Version 2.4.2

    注意此界面:

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

    此实施:

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

    如您所料,抛出编译错误。

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

    aCallback

    1 回复  |  直到 7 年前
        1
  •  2
  •   Community CDub    4 年前

    这不是一个bug。尾随参数较少的函数是 substitutable mentioned in the TypeScript FAQ .

    理由大致是:JavaScript中的任何函数都可以用任意数量的参数来调用,让调用方向函数发送比预期更多的参数总是安全的;你只是忽视他们。就你而言,

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

    有效,因为 aCallback.cb 忽略第一个参数,该参数必须是 string

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

    第二种情况, aSecondCallback ,无效,因为它会错误地将第一个参数解释为 number 而不是 事实上将是这样。这不安全,很可能是一个错误。


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


    @jbmilgrom公司 said

    谢谢你的回答。它在第一次阅读时完全有意义,但现在遇到了以下问题: aCallback.cb()

    这是一个编译错误,因为您声明 aCallback CallbackWithNameParameter 谁的 cb 一串 参数TypeScript不再知道或关心 aCallback公司 谁的 断路器

    如果您这样做了:

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

    这会奏效的。

    可替代性通常不是对称的:

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

    您可以将窄类型(没有参数的回调)分配给更宽的类型(只有一个参数的回调),而不会出错,但通常不能执行相反的操作。


    更新2

    @jbmilgrom公司 said

    aCallback公司 在实现时可以成功地定义为没有参数的空函数 CallbackWithNameParameter . 然而,它不能这样称呼,因为你知道,它是一种类型 CallbackWithNameParameter oneParam )永远不能 所以

    这是因为您通过声明更宽的类型使TypeScript忘记了。你倒了牛奶(窄型,像 CallbackWithNoParameter )放入标有“液体”的容器(较宽的类型,如 CallbackWithNameParameter ),然后试图打开容器,将液体倒入你的茶中,但没有检查它是否是漂白剂。你可能知道容器里有牛奶,但TypeScript只知道它是液体,并试图保护你。避免这种情况的最好方法是将牛奶倒入标有“牛奶”的容器中。如果有人稍后向你要液体,你可以给他们一个牛奶容器的内容物,但如果有人向你要牛奶,你不能给他们一个液体容器的内容物,除非有办法首先检查它是否是牛奶。

    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
    }