17 typescript
loyalvi Lv7

TypeScript

TypeScript

  • 接口
  • 泛型
  • 枚举
  • 交叉类型
  • 联合类型

TypeScript是什么

TypeScript 是一种由微软开发的开源编程语言。它是 JavaScript 的一个超集,意味着任何有效的 JavaScript 代码也是有效的 TypeScript 代码。TypeScript 的设计目标是提供静态类型检查和对 ES6+(ECMAScript 2015 及更高版本)特性的支持,以帮助开发者更有效地开发大型应用程序。以下是 TypeScript 的一些关键特性:

  1. 静态类型系统
    • TypeScript 引入了静态类型系统,允许开发者为变量、函数参数和返回值指定类型。这有助于在编译时期就发现潜在的类型错误。
  2. 接口和类型别名
    • 提供了接口(Interfaces)和类型别名(Type Aliases)来定义对象的形状,增强代码的可读性和可维护性。
  3. 类和模块
    • 支持基于类的面向对象编程,包括类的继承、实现和泛型。同时,TypeScript 支持 ES6 模块系统,使得代码模块化更加方便。
  4. 高级类型
    • 提供了多种高级类型,如联合类型、交叉类型、泛型等,以表达复杂的类型关系。
  5. 编译到 JavaScript
    • TypeScript 最终会被编译成 JavaScript 代码,这意味着它可以在任何支持 JavaScript 的环境中运行。
  6. 工具和集成
    • 拥有强大的工具支持,包括自动补全、类型检查、重构等。它与现代的编辑器和 IDE(如 Visual Studio Code)集成良好。
  7. 可选链和空值合并
    • 支持可选链(Optional Chaining)和空值合并(Nullish Coalescing)等现代 JavaScript 特性。
  8. 装饰器
    • 提供了装饰器(Decorators)的实验性支持,这是一种特殊类型的声明性函数,可以被附加到类声明、方法、访问器、属性或参数上。
  9. 类型推断
    • TypeScript 的编译器非常智能,能够根据上下文推断出变量的类型,减少类型注解的需要。
  10. 编译配置
  • 通过 tsconfig.json 文件来配置编译选项,如目标 ECMAScript 版本、模块系统、源映射等。
    TypeScript 由于其类型系统和对现代 JavaScript 特性的支持,已经成为许多大型项目的首选语言,尤其是在 Angular、React 和 Vue 等前端框架的开发中。它帮助开发者编写更健壮、更易于维护的代码,并减少运行时错误。

在TypeScript中,类(Class)是一种基本类型,它提供了一种方式来创建和使用对象。类的主要作用包括:

  1. 封装(Encapsulation)
    • 类可以将数据(属性)和行为(方法)封装在一起,隐藏内部实现细节,只暴露必要的接口给外部使用。
  2. 继承(Inheritance)
    • 类可以实现继承,允许一个类(子类)继承另一个类(父类)的属性和方法,这有助于代码复用和组织结构。
  3. 多态(Polymorphism)
    • 通过类的继承和接口实现,可以在不同的上下文中以统一的方式使用不同的对象,这使得代码更加灵活和可扩展。
  4. 代码复用
    • 通过继承,子类可以复用父类的代码,减少重复代码的编写。
  5. 组织结构
    • 类提供了一种结构化的方式来组织代码,使得代码更加模块化,易于理解和维护。
  6. 类型安全
    • TypeScript中的类可以定义属性和方法的类型,这有助于编译时检查类型错误,提高代码的健壮性。
  7. 接口实现
    • 类可以实现一个或多个接口,确保类实现特定的结构和行为。
  8. 抽象
    • 类可以被声明为抽象类,这意味着它们不能被直接实例化,而是用来作为其他类的基类。
  9. 访问修饰符
    • 类可以使用访问修饰符(如public, private, protected)来控制成员的可见性和访问级别。
  10. 静态成员
    • 类可以包含静态成员(属性和方法),这些成员属于类本身而不是类的实例。
  11. 构造函数
    • 类可以有一个或多个构造函数,用于在创建类的实例时初始化对象的状态。
  12. 析构函数(TypeScript 4.0+):
    • 类可以有一个析构函数,用于在对象被销毁时执行清理工作。
  13. 装饰器(Decorators):
    • TypeScript支持装饰器,这是一种特殊类型的声明,它可以被附加到类声明、方法、属性、参数等,提供额外的元数据和功能。
      类是TypeScript中面向对象编程的核心概念之一,它们提供了一种强大的工具来构建复杂的应用程序和库。通过使用类,开发者可以创建可重用、可维护和可扩展的代码。

TypeScript 的交叉类型

TypeScript 的交叉类型(Intersection Types)是一种高级类型,它允许将多个类型合并成一个新类型,使用符号 & 表示。交叉类型的主要特点是,一个值必须同时满足所有合并类型的要求。

语法

交叉类型的语法是通过 & 符号将多个类型组合在一起,例如:

1
type NewType = Type1 & Type2 & Type3;

基本用法

以下是一个简单的交叉类型示例:

1
2
3
4
5
6
7
8
9
10
11
interface Person {
name: string;
}
interface Employee {
employeeId: number;
}
type EmployeePerson = Person & Employee;
const john: EmployeePerson = {
name: "John Doe",
employeeId: 1234
};

在这个例子中,EmployeePerson 类型结合了 PersonEmployee 接口,因此 john 对象必须同时包含 nameemployeeId 属性。

属性冲突

当合并的类型中存在同名属性时,TypeScript 会根据以下规则处理:

  • 如果同名属性的类型相同,则合并后的属性类型保持不变。
  • 如果同名属性的类型不同,则合并后的属性类型为 never,表示该属性不可用。

函数中的交叉类型

交叉类型也可以用于函数参数或返回值类型。例如:

1
2
3
4
function printEmployeeDetails(employee: Person & Employee): void {
console.log(`Name: ${employee.name}`);
console.log(`Employee ID: ${employee.employeeId}`);
}

在这个例子中,printEmployeeDetails 函数的参数类型是 Person & Employee,因此传入的对象必须同时包含 nameemployeeId 属性。

应用场景

交叉类型常用于以下场景:

  1. 混入(Mixins):通过合并多个类的实例来创建一个新的对象,使其具备多个类的属性和方法。
  2. 合并对象属性:将多个对象的属性合并到一个对象中。

注意事项

  • 原子类型(如 stringnumber)之间不能进行交叉类型合并,因为它们合并后的类型是 never
  • 如果合并的类型中存在方法冲突,TypeScript 会报错。
    总之,交叉类型是一种强大的类型操作符,可以帮助我们创建复杂且具有多种特性的类型,适用于需要合并多个类型属性和方法的场景。

联合类型

TypeScript 中的联合类型(Union Types)是一种非常强大的类型系统特性,它允许一个变量可以是多种类型中的任意一种。联合类型通过使用 | 符号来组合多个类型,表示一个值可以是这些类型中的任意一个。

语法

联合类型的语法是通过 | 符号将多个类型组合在一起,例如:

1
type StringOrNumber = string | number;

基本用法

以下是一个简单的联合类型示例:

1
2
3
4
let value: string | number;
value = "Hello"; // OK
value = 123; // OK
value = true; // Error: Type 'boolean' is not assignable to type 'string | number'

在这个例子中,value 可以是 stringnumber,但不能是其他类型。

联合类型的属性访问

当使用联合类型时,TypeScript 会限制你只能访问那些在所有联合类型中都存在的属性或方法。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Bird {
fly: () => void;
layEggs: () => void;
}
interface Fish {
swim: () => void;
layEggs: () => void;
}
type BirdOrFish = Bird | Fish;
function getSmallPet(): BirdOrFish {
// ...
}
let pet = getSmallPet();
pet.layEggs(); // OK,因为 Bird 和 Fish 都有 layEggs 方法
pet.swim(); // Error: Property 'swim' does not exist on type 'BirdOrFish'

在这个例子中,pet 的类型是 Bird | Fish。由于 BirdFish 都有 layEggs 方法,因此可以调用 pet.layEggs()。但是,swim 方法只存在于 Fish 中,因此不能直接调用 pet.swim()

类型守卫

为了处理联合类型中的具体类型,通常需要使用类型守卫(Type Guards)来区分具体的类型。TypeScript 提供了几种类型守卫的方式:

1. 类型谓词(Type Predicate)

通过 typeofinstanceof 来检查类型:

1
2
3
4
5
6
7
8
function isFish(pet: BirdOrFish): pet is Fish {
return (pet as Fish).swim !== undefined;
}
if (isFish(pet)) {
pet.swim(); // OK,TypeScript 知道 pet 是 Fish 类型
} else {
pet.fly(); // OK,TypeScript 知道 pet 是 Bird 类型
}

2. 字面量类型守卫

如果联合类型中包含字面量类型,可以通过检查字面量值来区分类型:

1
2
3
4
5
6
7
8
type Shape = { kind: "circle"; radius: number } | { kind: "square"; sideLength: number };
function getArea(shape: Shape) {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2; // OK,TypeScript 知道 shape 是圆
} else {
return shape.sideLength ** 2; // OK,TypeScript 知道 shape 是正方形
}
}

联合类型的赋值规则

联合类型遵循以下赋值规则:

  • 如果一个值的类型是联合类型中的任意一个,那么它可以赋值给联合类型。
  • 如果一个值的类型是联合类型的子类型,那么它也可以赋值给联合类型。

联合类型与交叉类型的区别

  • 联合类型(Union Types):表示一个值可以是多种类型中的任意一种,使用 | 符号。
  • 交叉类型(Intersection Types):表示一个值必须同时满足多种类型的要求,使用 & 符号。

应用场景

联合类型在以下场景中非常有用:

  1. 函数参数类型:当函数参数可以接受多种类型时,可以使用联合类型。
    1
    2
    3
    function printId(id: number | string) {
    console.log(`Your ID is: ${id}`);
    }
  2. 对象类型:当对象可以是多种形状时,可以使用联合类型。
    1
    2
    3
    4
    5
    type Point2D = { x: number; y: number };
    type Point3D = { x: number; y: number; z: number };
    type Point = Point2D | Point3D;
    let point: Point = { x: 1, y: 2 }; // OK
    let point3D: Point = { x: 1, y: 2, z: 3 }; // OK
  3. 类型守卫:通过类型守卫区分联合类型中的具体类型,从而实现类型安全的操作。
  4. 字面量类型:联合类型常用于字面量类型,例如字符串字面量或数字字面量。
    1
    type Direction = "up" | "down" | "left" | "right";

注意事项

  • 联合类型中如果有 nullundefined,则需要显式地包含它们,否则会导致类型错误。
  • 联合类型中如果有重叠的属性或方法,TypeScript 会限制你只能访问那些在所有联合类型中都存在的属性或方法。
    总之,联合类型是 TypeScript 中非常灵活且强大的类型系统特性,它可以帮助我们处理多种类型的值,同时保持类型安全。

泛型

TypeScript 中的泛型(Generics)是一种强大的类型系统特性,允许在定义函数、接口或类时,不预先指定具体的类型,而是在使用时再指定类型。泛型的主要优点是提供了类型重用、类型安全性和灵活性。

泛型的基本使用

泛型函数

泛型函数允许用户为函数的类型参数传递一个或多个类型参数。这些类型参数可用于函数参数、函数返回值或函数体中的其他任何位置。

1
2
3
4
5
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("myString"); // 明确指定T为string类型
let output2 = identity(5); // 编译器自动推断T为number类型

泛型接口

在定义接口时,也可以使用泛型来创建可重用的组件。

1
2
3
4
5
6
7
8
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
let output = myIdentity(5); // 输出5,且类型是number

泛型类

在 TypeScript 中,也可以创建泛型类。

1
2
3
4
5
6
7
8
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = (x, y) => x + y;
let result = myGenericNumber.add(5, 10); // 输出15,且类型是number

泛型的应用场景

1. 通用函数

泛型函数通常用于处理不同类型的数据,但又不希望在每次调用时都明确指定类型。通过泛型,我们可以为函数提供更强的类型安全性。

1
2
3
4
5
6
7
function reverseArray<T>(items: T[]): T[] {
return items.reverse();
}
const reversedStringArray = reverseArray(["one", "two", "three"]);
console.log(reversedStringArray); // Output: ["three", "two", "one"]
const reversedNumberArray = reverseArray([1, 2, 3]);
console.log(reversedNumberArray); // Output: [3, 2, 1]

2. 容器类(集合类型)

泛型在容器类中尤为重要。例如,可以定义一个数组类,允许数组中的元素是任何类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Inventory<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
getAll(): T[] {
return this.items;
}
}
interface Book {
title: string;
author: string;
}
let bookInventory = new Inventory<Book>();
bookInventory.add({ title: "The Catcher in the Rye", author: "J.D. Salinger" });
console.log(bookInventory.getAll()); // 输出书籍的数组

3. 类型约束

泛型可以配合类型约束,限制泛型的具体类型,使得代码在灵活性和安全性之间取得平衡。

1
2
3
4
5
6
function logLength<T extends { length: number }>(item: T): void {
console.log(item.length);
}
logLength("Hello, TypeScript"); // Output: 16
logLength([1, 2, 3]); // Output: 3
// logLength(123); // Error: number does not have a length property

4. 接口和类

泛型不仅可以应用于函数,还可以应用于接口和类,进一步增强代码的复用性和类型安全性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface ApiResponse<T> {
data: T;
status: string;
message: string;
}
function handleApiResponse<T>(response: ApiResponse<T>) {
console.log(`Status: ${response.status}`);
console.log(`Message: ${response.message}`);
console.log("Data:", response.data);
}
const userResponse: ApiResponse<{ id: number; name: string }> = {
data: { id: 1, name: "Alice" },
status: "success",
message: "User fetched successfully"
};
handleApiResponse(userResponse);

泛型的高级应用

1. 联合类型与泛型

泛型和联合类型结合使用,可以允许函数处理多种类型的输入。

1
2
3
4
5
function combine<T, U>(a: T, b: U): T | U {
return a || b;
}
let result = combine(5, "hello");
console.log(result); // 输出 "hello"

2. 条件类型

TypeScript 4.1 引入了条件类型,它允许根据条件来决定类型。

1
2
3
type IsString<T> = T extends string ? "Yes" : "No";
type A = IsString<string>; // "Yes"
type B = IsString<number>; // "No"

总结

泛型是 TypeScript 中的一个强大工具,它可以使得代码更加灵活、类型安全并且可重用。通过泛型,函数、类、接口等结构可以处理不同类型的数据,而不牺牲类型检查的安全性。常见的应用场景包括通用的数据结构或容器类、高度可重用的函数和 API 请求处理等。

由 Hexo 驱动 & 主题 Keep
访客数 访问量