请教一个 typescript 的类型问题

2018-10-01 19:33:04 +08:00
 kernel
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 的类型系统坑多(当然也灵活的多)

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

```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/494457

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX