目录
1. class 类
1.1 class 关键字的基本使用
1.2 类继承
1.3 类成员可见性
1.4 类成员只读修饰符
2. 类型兼容性
2.1 类型兼容性
2.2 接口兼容性
2.3 函数兼容性
3. 交叉类型
4. 泛型
4.1 创建泛型函数
4.2 泛型约束
4.3 多个泛型的类型变量约束
4.4 泛型接口
4.5 泛型类
4.6 泛型工具类型
① 泛型工具类型 - Partial
② 泛型工具类型 - Readonly
③ 泛型工具类型 - Pick,>
④ 泛型工具类型 - Record,type>
5. 索引签名类型
6. 映射类型
概述:
- TS 中的高级类型有很多,重点学习以下高级类型:
- 1. class 类
- 2. 类型兼容性
- 3. 交叉类型
- 4. 泛型 和 keyof
- 5. 索引签名类型 和 索引查询类型
- 6. 映射类型
1. class 类
1.1 class 关键字的基本使用
- 根据 TS 中的类型推论,可以知道 Person 类的实例对象 p 的类型是 Person。
- TS 中的 class,不仅提供了 class 的语法功能,也作为一种类型存在。
- 声明成员 age,类型为 number(没有初始值)。
- 声明成员 gender,并设置初始值,此时,可省略类型注解(TS 类型推论 为 string 类型)。
- 成员初始化(比如,age: number)后,才可以通过 this.age 来访问实例成员。
- 需要为构造函数指定类型注解,否则会被隐式推断为 any;构造函数不需要返回值类型。
- 方法的类型注解(参数和返回值)与函数用法相同。
1.2 类继承
- extends(继承父类)
- implements(实现接口)
- JS 中只有 extends,而 implements 是 TS 提供的。
① extends(继承父类)
- 通过 extends 关键字实现继承。
- 子类 Dog 继承父类 Animal,则 Dog 的实例对象 dog 就同时具有了父类 Animal 和 子类 Dog 的所有属性和方法。
② implements(实现接口)
- 通过 implements 关键字让 class 实现接口。
- Person 类实现接口 Singable 意味着,Person 类中必须提供 Singable 接口中指定的所有方法和属性。
1.3 类成员可见性
- 可以使用 TS 来控制 class 的方法或属性对于 class 外的代码是否可见。
- public(公有的)
- protected(受保护的)
- private(私有的)。
- public:表示公有的、公开的,公有成员可以被任何地方访问,默认可见性。
- 在类属性或方法前面添加 public 关键字,来修饰该属性或方法是共有的。
- 因为 public 是默认可见性,所以,可以直接省略。
② protected(受保护的)
- protected:表示受保护的,仅对其声明所在类和子类中(非实例对象)可见。
- 在类属性或方法前面添加 protected 关键字,来修饰该属性或方法是受保护的。
- 在子类的方法内部可以通过 this 来访问父类中受保护的成员,但是,对实例不可见!
③ private(私有的)
- private:表示私有的,只在当前类中可见,对实例对象以及子类也是不可见的。
- 在类属性或方法前面添加 private 关键字,来修饰该属性或方法是私有的。
- 私有的属性或方法只在当前类中可见,对子类和实例对象也都是不可见的!
1.4 类成员只读修饰符
- 表示只读,用来防止在构造函数之外对属性进行赋值。
- 使用 readonly 关键字修饰该属性是只读的,注意只能修饰属性不能修饰方法。
- 注意:属性 age 后面的类型注解(比如,此处的 number)如果不加,则 age 的类型为 18 (字面量类型)。
- 接口或者 {} 表示的对象类型,也可以使用 readonly
2. 类型兼容性
2.1 类型兼容性
- Structural Type System(结构化类型系统)
- Nominal Type System(标明类型系统)。
- Point 和 Point2D 是两个名称不同的类。
- 变量 p 的类型被显示标注为 Point 类型,但是,它的值却是 Point2D 的实例,并且没有类型错误。
- 因为 TS 是结构化类型系统,只检查 Point 和 Point2D 的结构是否相同(相同,都具有 x 和 y 两个属性,属性类型也相同)。
- 但是,如果在 Nominal Type System 中(比如,C#、Java 等),它们是不同的类,类型无法兼容。
② 成员多的可以赋值给少的(成员少的可以兼容成员多的)
- 在结构化类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型,这种说法并不准确。
- 对于对象类型来说,y 的成员至少与 x 相同,则 x 兼容 y(成员多的可以赋值给少的)。
- Point3D 的成员至少与 Point 相同,则 Point 兼容 Point3D。
- 所以,成员多的 Point3D 可以赋值给成员少的 Point。
③ 除了 class 之外,TS 中的其他类型也存在相互兼容的情况,包括:
- 接口兼容性
- 函数兼容性 等。
2.2 接口兼容性
接口之间的兼容性,类似于 class。并且,class 和 interface 之间也可以兼容。
2.3 函数兼容性
函数之间兼容性比较复杂,需要考虑:
- 参数个数
- 参数类型
- 返回值类型。
① 参数个数
- 参数少的可以赋值给参数多的,所以,f1 可以赋值给 f2。
- 数组 forEach 方法的第一个参数是回调函数,该示例中类型为:(value: string, index: number, array: string[]) => void。
- 在 JS 中省略用不到的函数参数实际上是很常见的,这样的使用方式,促成了 TS 中函数类型之间的兼容性。
- 并且因为回调函数是有类型的,所以,TS 会自动推导出参数 item、index、array 的类型。
② 参数类型
- 函数类型 F2 兼容函数类型 F1,因为 F1 和 F2 的第一个参数类型相同。
- 注意,此处与前面讲到的接口兼容性冲突。
- 技巧:
- 将对象拆开,把每个属性看做一个个参数,则参数少的(f2)可以赋值给参数多的(f3)。
- 如果返回值类型是原始类型,此时两个类型要相同,比如,左侧类型 F5 和 F6。
- 如果返回值类型是对象类型,此时成员多的可以赋值给成员少的,比如,右侧类型 F7 和 F8
3. 交叉类型
交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)。
比如:
- 使用交叉类型后,新的类型 PersonDetail 就同时具备了 Person 和 Contact 的所有属性类型。
- 相同点:
- 都可以实现对象类型的组合。
- 不同点:
- 两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同。
- 以上代码,接口继承会报错(类型不兼容);交叉类型没有错误,可以简单的理解为
4. 泛型
- 函数、接口、class 中。
- 创建一个 id 函数,传入什么数据就返回该数据本身(也就是说,参数和返回值类型相同)。
4.1 创建泛型函数
- 语法:
- 在函数名称的后面添加 <>(尖括号),尖括号中添加类型变量,比如此处的 Type。
- 类型变量 Type,是一种特殊类型的变量,它处理类型而不是值。
- 该类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)。
- 因为 Type 是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有相同的类型。
- 类型变量 Type,可以是任意合法的变量名称。
- 语法:
- ·在函数名称的后面添加 <>(尖括号),尖括号中指定具体的类型,比如,此处的 number。
- 当传入类型 number 后,这个类型就会被函数声明时指定的类型变量 Type 捕获到。
- 此时,Type 的类型就是 number,所以,函数 id 参数和返回值的类型也都是 number。
- 在调用泛型函数时,可以省略 <类型> 来简化泛型函数的调用。
- 此时,TS 内部会采用一种叫做类型参数推断的机制,来根据传入的实参自动推断出类型变量 Type 的类型。
- 比如,传入实参 10,TS 会自动推断出变量 num 的类型 number,并作为 Type 的类型。
- 使用这种简化的方式调用泛型函数,使代码更短,更易于阅读。
- 当编译器无法推断类型或者推断的类型不准确时,就需要显式地传入类型参数。
4.2 泛型约束
- 默认情况下,泛型函数的类型变量 Type 可以代表多个类型,这导致无法访问任何属性。
比如,id('a') 调用函数时获取参数的长度:
- Type 可以代表任意类型,无法保证一定存在 length 属性,比如 number 类型就没有 length。
- 指定更加具体的类型
- 添加约束。
① 指定更加具体的类型
- 创建描述约束的接口 ILength,该接口要求提供 length 属性。
- 通过 extends 关键字使用该接口,为泛型(类型变量)添加约束。
- 该约束表示:传入的类型必须具有 length 属性。
- 传入的实参(比如,数组)只要有 length 属性即可,这也符合前面讲到的接口的类型兼容性。
4.3 多个泛型的类型变量约束
- 添加了第二个类型变量 Key,两个类型变量之间使用(,)逗号分隔。
- keyof 关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型。
- 本示例中 keyof Type 实际上获取的是 person 对象所有键的联合类型,也就是:'name' | 'age'。
- 类型变量 Key 受 Type 约束,可以理解为:Key 只能是 Type 所有键中的任意一个,或者说只能访问对象中存在的属性。
4.4 泛型接口
- 接口也可以配合泛型来使用,以增加其灵活性,增强其复用性。
- 在接口名称的后面添加 <类型变量>,那么,这个接口就变成了泛型接口。
- 接口的类型变量,对接口中所有其他成员可见,也就是接口中所有成员都可以使用类型变量。
- 使用泛型接口时,需要显式指定具体的类型(比如,此处的 IdFunc<nunber>)。
- 此时,id 方法的参数和返回值类型都是 number;ids 方法的返回值类型是 number[]。
- 当我们在使用数组时,TS 会根据数组的不同类型,来自动将类型变量设置为相应的类型。
- 可以通过 Ctrl + 鼠标左键(Mac:option + 鼠标左键)来查看具体的类型信息。
4.5 泛型类
- class 也可以配合泛型来使用。
- React.Component 泛型类两个类型变量,分别指定 props 和 state 类型。
- 类似于泛型接口,在 class 名称后面添加 <类型变量>,这个类就变成了泛型类。
- 此处的 add 方法,采用的是箭头函数形式的类型书写方式。
4.6 泛型工具类型
- TS 内置了一些常用的工具类型,来简化 TS 中的一些常见操作。
- 它们都是基于泛型实现的(泛型适用于多种类型,更加通用),并且是内置的,可以直接在代码中使用。
- Partial<Type>
- Readonly<Type>
- Pick<Type, Keys>
- Record<Keys, Type>
① 泛型工具类型 - Partial<Type>
泛型工具类型 - Partial<Type> 用来构造(创建)一个类型,将 Type 的所有属性设置为可选。
- 构造出来的新类型 PartialProps 结构和 Props 相同,但所有属性都变为可选的。
② 泛型工具类型 - Readonly<Type>
- 构造出来的新类型 ReadonlyProps 结构和 Props 相同,但所有属性都变为只读的。
③ 泛型工具类型 - Pick<Type, Keys>
- Pick 工具类型有两个类型变量:
- 表示选择谁的属性
- 表示选择哪几个属性。
- 其中第二个类型变量,如果只选择一个则只传入该属性名即可。
- 第二个类型变量传入的属性只能是第一个类型变量中存在的属性。
- 构造出来的新类型 PickProps,只有 id 和 title 两个属性类型。
④ 泛型工具类型 - Record<Keys,Type>
泛型工具类型 - Record<Keys,Type> 构造一个对象类型,属性键为 Keys,属性类型为 Type。
- Record 工具类型有两个类型变量:
- 表示对象有哪些属性
- 表示对象属性的类型。
- 构建的新对象类型 RecordObj 表示:
- 这个对象有三个属性分别为a/b/c,属性值的类型都是 string[]。
5. 索引签名类型
- 当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性),此时,就用到索引签名类型了。
- 使用 [key: string] 来约束该接口中允许出现的属性名称。表示只要是 string 类型的属性名称,都可以出现在对象中。
- 这样,对象 obj 中就可以出现任意多个属性(比如,a、b 等)。
- key 只是一个占位符,可以换成任意合法的变量名称。
- 隐藏的前置知识:
- JS 中对象({})的键是 string 类型的。
- MyArray 接口模拟原生的数组接口,并使用 [n: number] 来作为索引签名类型。
- 该索引签名类型表示:
- 只要是 number 类型的键(索引)都可以出现在数组中,或者说数组中可以有任意多个元素。
- 同时也符合数组索引是 number 类型这一前提。
6. 映射类型
映射类型:
- 基于旧类型创建新类型(对象类型),减少重复、提升开发效率。
- 映射类型是基于索引签名类型的,所以,该语法类似于索引签名类型,也使用了 []。
- Key in PropKeys 表示 Key 可以是 PropKeys 联合类型中的任意一个,类似于 forin(let k in obj)。
- 使用映射类型创建的新对象类型 Type2 和类型 Type1 结构完全相同。
- 注意:
- 映射类型只能在类型别名中使用,不能在接口中使用。
- 首先,先执行 keyof Props 获取到对象类型 Props 中所有键的联合类型即,'a' | 'b' | 'c'。
- 然后,Key in ... 就表示 Key 可以是 Props 中所有的键名称中的任意一个。
- keyof T 即 keyof Props 表示获取 Props 的所有键,也就是:'a' | 'b' | 'c'。
- 在 [] 后面添加 ?(问号),表示将这些属性变为可选的,以此来实现 Partial 的功能。
- 冒号后面的 T[P] 表示获取 T 中每个键对应的类型。比如,如果是 'a' 则类型是 number;如果是 'b' 则类型是 string。
- 最终,新类型 PartialProps 和旧类型 Props 结构完全相同,只是让所有类型都变为可选了。
刚刚用到的 T[P] 语法,在 TS 中叫做索引查询(访问)类型。
作用:用来查询属性的类型。
- Props['a'] 表示查询类型 Props 中属性 'a' 对应的类型 number。所以,TypeA 的类型为 number。
- [] 中的属性必须存在于被查询类型中,否则就会报错。
索引查询类型的其他使用方式:同时查询多个索引的类型
- 使用字符串字面量的联合类型,获取属性 a 和 b 对应的类型,结果为: string | number。
- 使用 keyof 操作符获取 Props 中所有键对应的类型,结果为: string | number | boolean。