V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
kernel
V2EX  ›  JavaScript

请教一个 typescript 的类型问题

  •  
  •   kernel · 2018-10-01 19:33:04 +08:00 · 6133 次点击
    这是一个创建于 2274 天前的主题,其中的信息可能已经有所发展或是发生改变。
    export {}
    
    type F = 
      ((a: string) => void) |
      ((b: boolean) => void)
    
    let f: F = (a: string) => {}
    f('foo')
    
    function f2 (f: F) {
      f('foo')
      ^^^^^
    }
    
    

    为什么光标处会出现这个错误呢?

    [ts] Cannot invoke an expression whose type lacks a call signature. Type 'F' has no compatible call signatures.

    PS. 唉头一次写 typescript 程序,碰到无数问题。typescript 要在本来无类型的 js 上面加类型搞出来的东西貌似比 java 的类型系统坑多(当然也灵活的多)

    16 条回复    2018-10-03 13:47:06 +08:00
    kernel
        1
    kernel  
    OP
       2018-10-01 19:53:44 +08:00
    额发现这里应该用&,才能得到正确的 overload 效果
    hst001
        2
    hst001  
       2018-10-01 20:04:17 +08:00
    这个逻辑看起来就有问题吧,参数类型是 F,然后你就直接 f('foo'),编译器怎么知道你到时候传进来的 f 是 (a: string) => void 还是 (b: boolean) => void ?
    kernel
        3
    kernel  
    OP
       2018-10-01 20:05:49 +08:00
    @hst001 F 是这二个函数 overload 在一起的效果,当然可以根据参数来自动判断
    把 type F =
    ((a: string) => void) |
    ((b: boolean) => void)
    改成
    type F =
    ((a: string) => void) &
    ((b: boolean) => void)

    就可以了
    kernel
        4
    kernel  
    OP
       2018-10-01 20:08:43 +08:00   ❤️ 1
    话说 typescript 开始起飞了,不建一个节点吗,毕竟和 js 还是完全不同的 @livid
    kernel
        5
    kernel  
    OP
       2018-10-01 20:14:27 +08:00
    @hst001 你说的是,用 | 的话是不知道传进来的具体是哪个,因为它不是重载函数了
    hst001
        6
    hst001  
       2018-10-01 20:25:59 +08:00
    @kernel #5 改成 & 表示取类型交集,换句话说,你等于告诉编译器,你传给 f2 的参数 f 是可以支持同时支持 string 和 boolean,所以就能编译通过了
    kernel
        7
    kernel  
    OP
       2018-10-01 20:49:48 +08:00
    @hst001 对,&才是函数重载,之前以为是|
    VDimos
        8
    VDimos  
       2018-10-01 22:49:18 +08:00 via Android   ❤️ 1
    这个和重载不重载无关,联合类型并不是或的意思,它本身就是一个独立的类型,所以不能直接调用,它没有 call signature。如果你使用交叉类型,两个函数签名交叉类型依旧是函数,所以有 call signature,才能编译。交叉类型不应该用在函数上,这个产生的结果很抽象,应该用在对象上面。你这个类型应该把参数改为 string 和 boolean 的联合类型,而不是独立成两个函数再联合
    NickCarter
        9
    NickCarter  
       2018-10-01 23:27:33 +08:00
    type F = (a: string | boolean) => void
    azh7138m
        10
    azh7138m  
       2018-10-01 23:58:28 +08:00
    楼上的写法是更好的选择
    关于重载,官方文档也有写示例,https://www.typescriptlang.org/docs/handbook/functions.html,可以写完声明,再去写具体的定义
    kernel
        11
    kernel  
    OP
       2018-10-02 00:18:25 +08:00   ❤️ 1
    @VDimos &用在多个函数上就是表示的是重载的意思吧。把参数改成 string | boolean 不是个办法,因为可能不同函数参数个数不一样
    VDimos
        12
    VDimos  
       2018-10-02 11:10:02 +08:00
    @kernel &用在函数上很抽象,我在官方文档里也没看见关于这个的讲解。你甚至可以在后面&一个任何类型,在编译时都会当作它有 call signature,但是实际上并没有
    kernel
        13
    kernel  
    OP
       2018-10-02 11:51:58 +08:00
    @VDimos 我是在搜 issue 列表发现&用在函数上就表示重载(而且不用&还真没有别的办法)。至于为什么一个函数可以&一个非函数而不报错,这可能是 ts 的缺陷吧。
    franklinyu
        14
    franklinyu  
       2018-10-03 10:50:36 +08:00
    「搜 issue 列表发现&用在函数上就表示重载」真的?哪个 issue ?很有兴趣知道
    kernel
        15
    kernel  
    OP
       2018-10-03 11:38:58 +08:00
    vsomeone
        16
    vsomeone  
       2018-10-03 13:47:06 +08:00
    不太懂这个重载是怎么写的… TypeScript 里的重载应该是下面这样吧

    ```typescript
    class Person {}
    class Pet {}
    class Shop {}

    type Shopping = {
    (a: Person): void;
    (b: Pet, c: Shop): void;
    };

    let shop: Shopping = (a?: Person, b?: Pet, c?: Shop) => {
    if (a instanceof Person) {}
    else if (b instanceof Pet && c instanceof Shop) {}
    }

    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2924 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 13:32 · PVG 21:32 · LAX 05:32 · JFK 08:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.