文章目录
- 一、TypeScript高级类型
- 1.1 class类
- 1.2 class继承
- 1.3 class类成员可见性
- 1.4 readonly
- 1.5 类型兼容性
- 1.5.1 对象之间的类型兼容性
- 1.5.2 接口之间类型兼容性
- 1.5.3 函数之间类型兼容性
- 1.6 交叉类型
- 1.7 交叉类型(&)和继承(extends)的对比
- 二、泛型
- 2.1 泛型约束--指定更具体的类型
- 2.2 泛型约束--extends添加约束
- 2.3 多个泛型变量
- 2.4 泛型接口
- 2.5 泛型类
- 2.6 泛型工具类Parital\<Type\>
- 2.7 泛型工具类Readonly\<Type>
- 2.8 泛型工具类Pick\<Type, Keys>
- 2.9 泛型工具类Record\<Keys, Type>
- 三、索引签名类型
- 四、映射类型
一、TypeScript高级类型
1.1 class类
TypeScrtip全面支持ES2015中引入的class关键字,并为其添加了类型注解和其他语句
class基本使用
TS中的class,不仅提供了class的语法功能,也作为一种类型存在
class Person {}
const p:Person = new Person()
实例属性初始化
class Person {
name: string = "kangyun";
gender = "男";
}
const p: Person = new Person();
构造函数
构造函数:为class类的实例属性初始化值的
构造函数不需要返回值
class Person {
name: string;
gender: string;
constructor(name: string, gender: string) {
this.name = name;
this.gender = gender;
}
}
const p: Person = new Person('张三', '男');
</script>
类的实例方法
class Person {
name: string;
gender: string;
constructor(name: string, gender: string) {
this.name = name;
this.gender = gender;
}
// 方法的类型注解(参数和返回值)与函数用法相同
sayHello(name: string): void {
console.log(`hello,${name}`);
}
}
const p: Person = new Person("张三", "男");
p.sayHello("李四");
1.2 class继承
类继承的两种方式:
- extends(继承父类)
- implements(实现接口)
JS中只有extends,而implements是TS提供的
class Animail {
move() {
console.log("Moving!");
}
}
class Dog extends Animail {
bark() {
console.log("汪汪......");
}
}
// 子类Dog继承父类Animal,则Dog的实例对象dog就同时具有了父类
// Animal和子类Dog的所有属性和方法
const dog: Dog = new Dog();
dog.move();
dog.bark();
interface Singable {
name: string;
sing: () => void;
}
// Person类实现接口Singable就必须提供Singble接口中指定的方法和属性
class Person implements Singable {
name = "张三";
sing() {
console.log("开始黄牛叫了......");
}
}
1.3 class类成员可见性
可见性修饰符包括:
- public 公有的
- proected 受保护的
- private 私有的
public表示公共、公有的,公有成员可以被任何地方访问
public: 是默认可见性,所以可以直接少略
class Animal{
public move(){
console.log('moving!')
}
}
protected: 表示受保护的,仅对其所在类和子类中(非实例对象)可见
在子类的方法内部可以通过this来访问父类中受保护的成员,但是对实例不可见
class Animal {
protected move() {
console.log("moving");
}
}
class Dog extends Animal {
bark() {
console.log("汪汪......");
this.move();
}
}
const dog = new Dog();
dog.bark();
private: 表示私有的,只在当前类中可见,对实例对象以及子类也是不可见的
class Animal {
private move() {
console.log("moving");
}
walk(){
this.move()
}
}
1.4 readonly
readonly表示只读,用来防止在构造函数之外对属性进行赋值
class Animal {
readonly age: number;
constructor(age: number) {
this.age = age;
}
}
- readonly只能修饰属性不能修饰方法
- 接口或者{}表示的对象类型,也可以使用readonly修饰
1.5 类型兼容性
两种类型系统
- Structural Type System(结构化类型系统)
- Nominal Type System(标明类型系统)
TS采用的是结构化类型系统,类型检查关注的是值所具有的形状
在结构类型系统中如果两个对象具有相同的形状态,则认为它们属性同一类型
class Point01{x: number, y: number}
class Point02{x: number, y: number}
const p:Poing01 = new Point02()
因为TS是结构化类型系统,只检查Point01和Point02的结构是否相同(相同,都具有x和y两个属性,属性类型也相同)
但是如果在标明类型系统(如c#和java中)它们是不同的类,类型无法兼容。
1.5.1 对象之间的类型兼容性
对于对象类型来说,y的成员至少与x相同,则x兼容y(成员多的可以赋值给少的)
class Point {x: number; y: number}
class Point3D {x: number; y:number: z:number}
const p:Point = new Point3D()
1.5.2 接口之间类型兼容性
接口之间的兼容性类似于class,并且class和interface之间也可以兼容
interface Point {x:number; y:number}
interface Point2D {x:number; y:number}
let p1: Point
let p2: Point2D = p1
interface Point3D {x:number; y:number; z:number}
let p3:Point3D
p2 = p3
1.5.3 函数之间类型兼容性
函数之间兼容性比较复杂,需要考虑参数个数、参数类型、返回值类型
参数个数,参数多的兼容参数少的(或者说参数少的可以赋值给多的)
type F1 = (a:number)=>void
type F2 = (a:number, b:number) => void
let f1: F1
let f2: F2 = F1
- 参数少的可以赋值给参数多的,所以f1可以赋值给f2
- 数组forEach方法的第一个参数是回调函数,该示例中类型为(value: string, index: number, array: string[])=>void
- 在JS中省略用不到的函数参数实际是很常见的,这样的使用方式,促成了TS中函数类型之间的兼容性
- 并且因为回调函数是有类型的,所以TS会自动推导出参数item、index、array的类型
***参数类型,***相同位置的参数类型要相同
type F1 = (a:number) => string
type F2 = (a:number) => string
let f1: F1
let f2: F2 = F1
函数类型F2兼容函数类型F1,因为F1和F2的第一个参数类型相同。
interface P2D {x:number; y:number}
interface P3D {x:number; y:number; z:number}
type F2 = (p:P2D)=>void
type F3 = (p:P3D)=>void
left f2: F2
let f3: F3 = f2
技巧:将对象拆开,把每个属性看做一个一个的参数,则参数少的f2可以赋值给参数多的f3。
返回值类型,只关注返回值类型本身即可
type F5 = () => string
type F6 = () => string
let f5: F5
let f6: F6 = f5
type F7 = () => {name: string}
type F8 = () => {name: string, age: number}
let f7: F7
let f8: F8
// 返回的是对象,对象多的可以赋值给少的
f7 = f8
1.6 交叉类型
交叉类型(&):类似于接口继承,用于组合多个类型为一个类型(常用于对象类型)
interface Person {name: string}
interface Contact {phone: string}
type PersonDetail = Person & Contact
let obj: PersonDetail = {
name: 'Bill',
phone: '15220......'
}
使用交叉类型后,新的类型PersonDetail就同时具备了Person和Contact的所有属性类型,这和继承很相似。
1.7 交叉类型(&)和继承(extends)的对比
相同点:都可实现对象类型的组合
不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同。
interface A {
fn:(value: number) => string
}
interface B extends A {
fn: (value: string) => string
}
上示代码接口继承会报错(类型不兼容)
interface A{
fn:(value: number) => string
}
interface B{
fn:(value: string) => string
)
type C = A & B
C的结果会是: fn:(value: number|string) => string
二、泛型
泛型是可以在保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用,常用于函数、接口、class中。
泛型是用来实现可复用组件功能的主要工具之一。
创建泛型函数
function id<Type>(value: Type):Type { return value}
PS:
在函数名称后面添加<>尖括号,尖括号中添加类型变量,如此处Type,类型变量Type,是一种特殊类型的变量,它处理类型而不是值。该类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)。因为Type是类型,因此可以将其作为函数参数和返回值的类型,类型变量Type,可以是任意合法的变量名称。
调用泛型函数
const num = id<number>(10)
const str = id<string>('hello')
PS:
当传入类型number后,这个类型就能会被函数声明时指定的类型变量Type捕获到。这样,通过泛型就做到了让id函数与多种不同的类型一起工作,实现了复用的同时保证了类型安全。
简化调用泛型函数
function id<Type>(value: Type):Type { return value}
let num01 = id<number>(10)
let num02 = id(10) //简化
简化由TS类型参数推断,当编译器无法推断类型或者推断的类型不准确时,就需要显式地传入类型参数。
2.1 泛型约束–指定更具体的类型
***泛型约束:***默认情况下,泛型函数的类型变量Type可以代表多个类型,这导致无法访问任何属性。
如:id(‘a’)调用函数时获取参数的长度
function id<Type>(value: Type): Type{
console.log(value.length)
return value
}
PS:
Type可以代表任意类型,无法保证一定存在length属性,如number就没有length,此时就需要为泛型添加约束来收缩类型。
指定更具体的类型
function id<Type>(value: Type[]): Type[]{
console.log(value.length)
return value
}
将类型Type修改为Type[](Type类型的数组),因为只要是数组就一定存在Length。
2.2 泛型约束–extends添加约束
添加约束
interface ILength{ length: number }
function id<Type extends ILength>(value: Type): Type{
console.log(value.length)
return value
}
PS:
通过extends关键字使用该接口,为泛型(类型变量)添加约束,该约束表示:传入的类型必须具有length属性
2.3 多个泛型变量
泛型的类型变量可以有多个,并且类型变量之间还可以约束。
// Key extends keyof Type
// Key的值必须是Type类的key
function getProp<Type, Key extends keyof Type>(obj: Type, key: Key){
reutnr obj[key]
}
let person = {name: 'kk', age: 18 }
getProp(person, 'name')
keyof关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型
2.4 泛型接口
泛型接口:接口也可以配合泛型来使用,以增加其灵活性,增强复用性
interface IdFunc<Type>{
id: (value: Type) => Type
ids: () => Type[]
}
let obj:IdFunc<number>{
id(value){return value},
ids(){ return [1, 2, 3] }
JS中的数组在TS中就是一个泛型接口
const strs = ['a', 'b', 'c']
2.5 泛型类
class GenericNumber<NumberType>{
defaultValue: NumberType
add: (x: NumberType, y: NumberType) => NumberType
}
PS:
类似于泛型接口,在class名称后添加<类型变量>这个类就成了泛型类,add方法采用的是箭头函数形式书写方式。
const myNum = new GenericNumber<number>()
myNum.defaultValue = 10
2.6 泛型工具类Parital<Type>
泛型工具类Parital<Type>用来构造(创建)一个类型,将Type的所有属性设置为可选。
interface Props {
id: number,
children: number[]
}
type ParitalProps = Parital<Props>
PS:构造出来的新类型ParitalProps结构和Props相同,但所有属性都变为可选的。
2.7 泛型工具类Readonly<Type>
泛型工具类Readonly<Type>用来构造一个类型,将Type的所有属性都设置为readonly(只读)
interface Props{
id: string
children: number[]
}
type ReadonlyProps = Readonly<Props>
解释:构造出来的新类型ReadonlyProps结构和Props相同,但所有属性都变为只读
let props: ReadonlyProps = {id: '1', children: [] }
props.id = '2' //error
重新给id属性赋值,会报错,因为它是只读属性。
2.8 泛型工具类Pick<Type, Keys>
泛型工具类Pick<Type, Keys>从Type中选择一组属性来构造新类型
interface Props{
id: string
title: string
children: number
}
type PickProps = Pick<Props, 'id'|'title'>
Pick工具类型有两个类型变量:
- 表示选择谁的属性
- 表示要选择哪几个属性
构造出来的PickProps只有id和title两个属性类型。
2.9 泛型工具类Record<Keys, Type>
泛型工具类Record<Keys, Type>构造一个对象类型,属性键为Keys,属性类型为Type
type RecordObje = Record<'a'|'b'|'c', string[]>
let obj: RecordObje = {
a: ['a'],
b: ['2'],
c: ['aaa]
}
PS:
Record工具类型有两个类型变量
1、表示对象有哪些属性
2、表示对象属性的类型
三、索引签名类型
绝大多数情况下,我们都可以在使用对象前就确定对象的结构,并为对象添加准确的类型。
当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性)此时就用到索引签名类型了。
interface AnyObject{
[key: string]: number
}
let obj: AnyObject = {
a: 1,
b: 2,
}
PS:
使用[key: string]来约束该接口中允许出现的属性名称。表示只要是string类型的属性名称,都要以出现在对象中。
key只是一个占位符,可以换成任意合法的变量名称。
四、映射类型
映射类型:基于旧类型创建新类型(对象类型)
type PropKeys = 'x' | 'y' | 'z'
type Type1 = { x:number; y: number; z: number }
上例有太多重复的代码
type PropKeys = 'x' | 'y' | 'z'
type Type2 = { [Key in PropKeys]: number}
映射类型是基于索引签名类型的,所以该语法类似于索引签名类型
映射类型只能在类型别名中使用,不能在接口中使用
映射类型keyof
映射类型除了根据联合类型创建新类型外,还可以根据对象类型来创建
type Props = {a: number; b: string; c: boolean}
type Type = { [key in keyof Props]: number }
PS:
先执行keyof Props获取到对象类型Props中所有键的联合类型即’a’|‘b’|‘c’
然后key in …就表示key可以是Props中所有的键名称中的任意一个
像泛型工具类型Parital<Type>这些都是基于映射类型实现的
type Parital<T> = {
[P in keyof T]?: T[P]
}
T[p]在TS中叫做查询(访问)类型
用来查询属性的类型
type Props = {a: number; b: string; c: boolean}
type TypeA = Props['a'] // type TypeA = number
注意:[]中的属性必须存在于被查询类型中,否则就会报错。
索引查询类型的其他使用方式:同时查询多个索引的类型
type Props = {a: number; b: string; c: boolean}
type TypeA = Props['a'|'b']
使用字符串字面量的联合类型,获取属性a和b对应的类型结果为string|number
type TypeA = Props[keyof Props]
使用keyof操作符获取Props中所有键对应的类型,结果为number|string|boolean