extends
是 typeScript 中的关键字。在 typeScript 的类型编程世界里面,它所扮演的角色实在是太重要了,所以,我们不得不需要重视它,深入学习它。在我看来,掌握它就是进入高级 typeScript 类型编程世界的敲门砖。但是,现实是,它在不同的上下文中,具体不同的,相差很大的语义。如果没有深入地对此进行梳理,它会给开发者带来很大的困惑。梳理并深入学习它,最后掌握它,这就是我编写这篇文章的初衷。
extends 的几个语义
让我们开门见山地说吧,在 typeScript 在不同的上下文中,extends
有以下几个语义。不同语义即有不同的用途:
- 用于表达类型组合;
- 用于表达面向对象中「类」的继承
- 用于表达泛型的类型约束;
- 在条件类型(conditional type)中,充当类型表达式,用于求值。
extends 与 类型组合/类继承
extends
可以跟 interface
结合起来使用,用于表达类型组合。
示例 1-1
interface ChildComponentProps { onChange: (val: string)=> void } interface ParentComponentProps extends ChildComponentProps { value: string }
在 react 组件化开发模式中,存在一种自底向上的构建模式 – 我们往往会先把所有最底层的子组件的 props
构建好,最后才定义 container component
(负责提升公共 state,聚合和分发 props) 的 props
。此时,inferface 的 extends
正好能表达这种语义需求 – 类型的组合(将所有子组件的 props
聚合到一块)。
当然,interface
的 extends
从句是可以跟着多个组合对象,多个组合对象之间用逗号,
隔开。比如ParentComponentProps
组合多个子组件的 props
:
示例 1-2
interface ChildComponentProps { onChange: (val: string)=> void } interface ChildComponentProps2 { onReset: (value: string)=> void } interface ParentComponentProps extends ChildComponentProps, ChildComponentProps2 { value: string }
注意,上面指出的是「多个组合对象」,这里也包括了Class
。对,就是普通面向概念中的「类」。也就是说,下面的代码也是合法的:
示例 1-3
interface ChildComponentProps { onChange: (val: string)=> void } interface ChildComponentProps2 { onReset: (value: string)=> void } class SomeClass { private name!: string // 变量声明时,变量名跟着一个感叹号`!`,这是「赋值断言」的语法 updateName(name:string){ this.name = name || '' } } interface ParentComponentProps extends ChildComponentProps, ChildComponentProps2, SomeClass { value: string }
之所以这也是合法的,一切源于一个特性:在 typeScript 中,一个 class 变量既是「值」也是「类型」。在interface extends class
的上下文中,显然是取 class 是「类型」的语义。一个 interface extends
另外一个 class,可以理解为 interface 抛弃这个 class 的所有实现代码,只是跟这个 class 的「类型 shape」 进行组合。还是上面的示例代码中,从类型 shape 的角度,SomeClass
就等同于下面的 interface:
示例 1-4
interface SomeClass { name: string updateName: (name:string)=> void }
好了,