目录

1. 接口
1.1 接口的声明
1.2 接口的继承
1.3 接口的实现
2. 字面量类型和keyof关键字
2.1 字面量类型
2.2 keyof 关键字
3. 类型保护与自定义类型保护
3.1 类型保护
3.2 自定义类型保护
4. 定义泛型和泛型常见操作
4.1 认识泛型
4.2 基本补充
4.3 泛型接口
4.4 泛型类
4.5 泛型约束
5. 类型兼容性
6. 映射类型与内置工具类型
6.1 映射类型
6.2 内置工具类型
Partial
ReadOnly
Pick
Record
Required
Omit
Exclude
Extract
NonNullable
Parameters
ReturnType
7.1 类的定义
7.2 类的继承
7.3 类的成员修饰符
7.4 只读属性readonly
7.5 getters/setters
7.6 static 静态成员
4.7 抽象类

1. 接口

接口是一系列抽象方法的声明,是一些方法特征的集合。简单来说,接口的作用就是为这些类型命名和为你的代码或第三方代码契约

1.1 接口的声明

typescript
interface Person { name: string, age?: number, // 可选属性 readonly height: number, // 只读属性 [index: number]: string, // 索引签名 }

1.2 接口的继承

接口可以多继承

ts
interface Student extends Person,Animal { sno: number }

1.3 接口的实现

  • 接口定义后,也是可以被类实现,通过implements关键字实现接口
    • 一个类可以实现多个接口,并且必须实现接口中对应的属性和方法;
    • 如果被一个类实现,那么在之后需要传入接口的地方,都可以将这个类传入;
typescript
class Student implements Person { name: string; sno: string; constructor(name: string, sno: string) { this.name = name; this.sno = sno; } } function study(p: Person) { console.log(p); } const s = new Student("coder", "001"); study(s);

接口和类型别名的区别

  • 别名可以给基本类型,接口后面只能接对象类型
  • 接口可以合并(重复的对某个接口来定义属性和方法),而别名不能
  • 接口具备继承能力,而别名不行
  • type具备映射类型,接口不支持

2. 字面量类型和keyof关键字

2.1 字面量类型

typescript
const message: 'hello' = 'hello'

默认情况下这么做是没有太大的意义的,但是我们可以将多个类型联合在一起;

typescript
type Alignment = 'right' | 'left' | 'center' const align: Alignment = 'right'

2.2 keyof 关键字

TS
interface A { username: string; age:number } let a: keyof A // age | usernmae

keyof 关键字可以获取对象中的所有键类型组成的联合类型。

3. 类型保护与自定义类型保护

3.1 类型保护

类型保护允许你使用更小范围下的对象类型

看下图报错,因为n可能是字符串也可能是数字,所以ts会检测错误 image.png

通过typeof判断n为string使其可以调用length属性,这就是ts的类型保护 image.png

常见的类型保护:

  • in
  • instanceof
  • typeof
  • 字面量类型

3.2 自定义类型保护

image.png

4. 定义泛型和泛型常见操作

4.1 认识泛型

  • 泛型:将类型进行参数化
    • 需要在这里使用一种特性的变量 - 类型变量(type variable),它作用于类型,而不是值
typescript
function foo<T>(arg: T): T { return arg }

上面的函数可以使用两种方式来调用它:

  • 方式一:通过 <类型> 的方式将类型传递给函数
  • 方式二:通过类型推到,自动推到出我们传入变量的类型
typescript
// 方式一 console.log(foo<string>('hello')) console.log(foo<number>(1)) // 方式二 console.log(foo('hello')) console.log(foo(1))

4.2 基本补充

可以传入多个类型:

function foo<T,K>(a1: T, a2: K) { }
  • 平时在开发中我们可能会看到一些常用的名称:
    • T:Type的缩写,类型
    • K、V:key和value的缩写,键值对
    • E:Element的缩写,元素
    • O:Object的缩写,对象

4.3 泛型接口

typescript
interface IPerson<T> { name: T, friends: T[] foo: (num: T) => void }

4.4 泛型类

typescript
class Person<T> { x: T y: T constructor(x: T, y: T) { console.log(x, y) } }

4.5 泛型约束

  • 有时候我们希望传入的类型有某些共性,但是这些共性可能不是在同一种类型中:
    • 比如string和array都是有length的,或者某些对象也是会有length属性的;
    • 那么只要是拥有length的属性都可以作为我们的参数类型,那么应该如何操作呢?
typescript
interface ILength { length: number; } function getLength<T extends ILength>(l: T) { console.log(l.length); } getLength("123"); getLength([1, 2, 3]);

5. 类型兼容性

类型兼容性用于确定一个类型是否能赋值给其他类型

ts
let a: number = 123; let b: string | number = "hello"; // a = b; // 不能将b赋值给a 因为b有可能是字符串 // b = a; // 可以将a赋值给b
ts
let a: { username: string } = { username: "123" }; let b: { username: string; age: number } = { username: "123", age: 123 }; //a = b // success // b = a // error

6. 映射类型与内置工具类型

6.1 映射类型

可以将已知类型的每个属性都变为可选的或者可读的

TS
type A = { username: string, age: 24 } type B<T> = { [P in keyof T]: T[P] // in 相当于循环遍历了泛型T中的类型 } type C = B<A> // 别名C和别名A具有相同的类型

6.2 内置工具类型

Partial

Partial<T> 的作用就是将某个类型里的属性全部变为可选项 ? 。

TS
// 源码 type Partial<T> = { [P in keyof T]?: T[P]; }; interface Foo { name: string age: number } type Bar = Partial<Foo> // 相当于 type Bar = { name?: string age?: number }

ReadOnly

Readonly<T> 的作用是将某个类型所有属性变为只读属性,也就意味着这些属性不能被重新赋值。

TS
// 源码 type Readonly<T> = { readonly [P in keyof T]: T[P]; }; interface Foo { name: string age: number } type Bar = Readonly<Foo> // 相当于 type Bar = { readonly name: string readonly age: number }

Pick

Pick<T, K extends keyof T> 的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型。

ts
// 源码 type Pick<T, K extends keyof T> = { [P in K]: T[P]; }; interface Foo { name: string; age?: number; gender: string; } type Bar = Pick<Foo, 'age' | 'gender'> // 相当于 type Bar = { age?: number gender: string } const todo: Bar= { age?: 3, gender: 男 }; console.log(todo)

Record

Record<K extends keyof any, T> 的作用是将 K 中所有的属性的值转化为 T 类型。

ts
// 源码 type Record<K extends keyof any, T> = { [P in K]: T; }; type E = Record<"username" | "age", string>; // 相当于 type E = { username: string; age: string; }

Required

Required<T> 的作用就是将某个类型里的属性全部变为必选项。

ts
type Required<T> = { [P in keyof T]-?: T[P]; }; type A = { username?: string, age?: number } type B = Required<A> // 相当于 type B = { username: string; age: number; }

Omit

Pick 是相反的操作,Omit<T, K extends keyof any> 的作用是使用 T 类型中除了 K 类型的所有属性,来构造一个新的类型。

ts
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>; type A = { username: string, age: number } type B = Omit<A, 'username'> //相当于 type B = { age: number; }

Exclude

Exclude<T, U> 的作用是将 T 中的属于 U 的类型移除掉

TS
type Exclude<T, U> = T extends U ? never : T; type A = Exclude<string | number | boolean, string>; // 相当于 type A = number | boolean

Extract

Extract<T, U> 的作用是从 T 中提取出 U 。

ts
type Extract<T, U> = T extends U ? T : never; type A = Extract<string | number | boolean, string>; // 相当于 type A = string

NonNullable

NonNullable<T> 的作用是用来过滤类型中的 null 及 undefined 类型。

TS
type NonNullable<T> = T & {}; type A = NonNullable<string | null | undefined>; // 相当于 type A = string

Parameters

Parameters<T> 的作用是用于获得函数的参数类型组成的元组类型。

ts
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never; type Foo = (n: number, m: string) => void; type a = Parameters<Foo> // 相当于 type a = [n: number, m: string]

ReturnType

ReturnType<T> 的作用是用于获取函数 T 的返回类型。

TS
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any; type Foo = (n: number, m: string) => number; type a = ReturnType<Foo> // 相当于 type a = number

7. 类中如何使用类型

7.1 类的定义

  • 使用class关键字来定义一个类;
  • 声明一些类的属性:在类的内部声明类的属性以及对应的类型
    • 如果类型没有声明,那么它们默认是any的;
    • 也可以给属性设置初始化值;
    • 在默认的strictPropertyInitialization模式下面属性是必须初始化的,如果没有初始化,那么编译时就会报错;如果想要不初始化,可以使用 name!: string语法
  • 类可以有自己的构造函数constructor,当我们通过new关键字创建一个实例时,构造函数会被调用;
    • 构造函数不需要返回任何值,默认返回当前创建出来的实例;
  • 类中可以有自己的函数,定义的函数称之为方法;
typescript
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } eating() { console.log(this.name + " is eating"); } } const p = new Person("coder", 22); p.eating();

7.2 类的继承

  • 面向对象的其中一大特性就是继承,继承不仅仅可以减少代码量,也是多态的使用前提
  • 使用extends关键字来实现继承,子类中使用super来访问父类
typescript
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } eating() { console.log(this.name + " is eating"); } } class Student extends Person { sno: string; constructor(name: string, age: number, sno: string) { super(name, age); this.sno = sno; } studying() { console.log("学号是" + this.sno + "的学生正在学习"); } } const s = new Student("yjw", 22, "001"); s.eating(); s.studying();
  • Student类可以有自己的属性和方法,并且会继承Person的属性和方法;

  • 在构造函数中,通过super来调用父类的构造方法,对父类中的属性进行初始化;

7.3 类的成员修饰符

  • 在TypeScript中,类的属性和方法支持三种修饰符: public、private、protected
    • public 修饰的是在任何地方可见、公有的属性或方法,默认编写的属性就是public的;
    • private 修饰的是仅在同一类中可见、私有的属性或方法;
    • protected 修饰的是仅在类自身及子类中可见、受保护的属性或方法;
  • public是默认的修饰符,也是可以直接访问的,这里来演示一下protected和private。
typescript
class Person { private name: string; protected age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } eating() { console.log(this.name, this.age); } } class Student extends Person { sno: string; constructor(name: string, age: number, sno: string) { super(name, age); this.sno = sno; } } const p = new Person("coder", 22); // console.log(p.name); // × 编译错误 属性“name”为私有属性,只能在类“Person”中访问。 // console.log(p.age); // × 编译错误 属性“age”受保护,只能在类“Person”及其子类中访问。 p.eating(); // √

7.4 只读属性readonly

  • 如果有一个属性不希望外界可以任意的修改,只希望确定值后直接使用,那么可以使用readonly:
typescript
class Person { readonly name:string constructor(name: string) { this.name = name } } const p = new Person('coder') p.name = 'coderyjw' // × 无法分配到 "name" ,因为它是只读属性

7.5 getters/setters

在前面一些私有属性我们是不能直接访问的,或者某些属性我们想要监听它的获取(getter)和设置(setter)的过程,

这个时候我们可以使用存取器。

typescript
class Person { private _name: string; constructor(name: string) { this._name = name; } get name(): string { return this._name; } set name(newName) { this._name = newName; } } const p = new Person("coder"); p.name = 'coderyjw' console.log(p.name);

7.6 static 静态成员

用于定义类级别的成员和方法。

typescript
class Person { static time: string = "2021-12-26"; } console.log(Person.time);

4.7 抽象类

typescript
// abstract定义的类叫抽象类 // 抽象类不能被实例化,抽象类 // 抽象类中必须要有抽象方法 abstract class Shape { // abstract定义的函数叫抽象函数 // 抽象方法,必须存在于抽象类中; // 抽象方法必须被子类实现,否则该类必须是一个抽象类; abstract getArea(): number; } class Circle extends Shape { r: number; constructor(r: number) { super(); this.r = r; } getArea(): number { return this.r * this.r * Math.PI; } } const c = new Circle(5); console.log(c.getArea());
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:叶继伟

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!