求助 TypeScript 中关于函数参数类型约束的问题

2022-04-14 15:45:50 +08:00
 johnkiller
type Assignable<T, U> = T extends U ? true : false

type Father = (a: Record<string, any>) => void
type Son = (a: { count: number }) => void


type R1 = Assignable<Son, Father> // false
type R2 = Assignable<Father, Son> // true

type R3 = Assignable<Parameters<Son>, Parameters<Father>> // true
type R4 = Assignable<{ count: number }, Record<string, any>> // true

{ count: number }可以赋给( TypeScript 官方叫它 assignable )类型Record<string, any>我能理解没问题,
但为什么放到函数的参数里就反过来了呢?
Son 不应该也是 Father 的子类型吗?

附上 playgorund: https://reurl.cc/12nVGV

我的逻辑思维可能太差了💀
提前感谢点开此贴🙏

804 次点击
所在节点    问与答
6 条回复
noe132
2022-04-14 16:01:58 +08:00
这是 Covariance and contravariance 的区别。
比如我们说 dog extends animal
如果一个函数调用接受一个 animal ,那么你传 dog 是没有问题的。
但是如果一个函数调用接受一个 callback ,这个 callback 会收到一个 animal ,如果你传一个收到 dog 的 callback 就不行。因为 callback 的调用方有可能会给你一个 animal ,但 animal 不是 dog 。
针对类型 T ,如果说 T<dog> extends T<animal> 则 T 是 covarient 。如果 T<animal> extends T<dog>,则 T 是 contravariant 。在你这个例子里,T = (v: T) => void
noe132
2022-04-14 16:04:03 +08:00
最后一句有些错误,应该是:在你这个例子里,type T<U> = (v: U) => void
noe132
2022-04-14 16:09:49 +08:00
还有一些比较特殊的例子是 invarient 的。最常见的就是可写数组。因为数组的读操作是 corarient ,而写是 contravarient ,2 者结合起来就是 invarient 。
Array<Dog> 可以 push 一个 GrayHound 进去,也可以 pop 一个 animal 出来,但是不能赋值给 Array<Animal> 或者 Array<GrayHound> 的变量,因为前者可能会 push 一个 dog 进去,后者可能会 pop
一个 dog 出来。
chenluo0429
2022-04-14 16:18:30 +08:00
举个栗子:
Farther 类型对应的是可以给所有动物洗澡的机器,参数是所有动物。
Son 类型是可以给狗洗澡的机器,参数是狗。
如果你需要一个动物,我可以给你只狗。
如果你需要一台可以给狗洗澡的机器,我可以给你一台能给所有动物洗澡的机器,但是反过来就不对了。
enchilada2020
2022-04-14 18:13:52 +08:00
johnkiller
2022-04-14 21:44:47 +08:00
@chenluo0429
@enchilada2020
@noe132

非常感谢三位,总算看明白了。

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

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

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

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

© 2021 V2EX