高阶用法
泛型(Generics)
泛型允许您编写可重用的代码,以处理多种类型的数据。它们是在编译时确定的,可以帮助您在编译时捕获类型错误。
- 泛型函数
function identity<T>(arg: T): T {
return arg;
}
// 使用泛型函数
let result = identity<string>('Hello');
console.log(result); // Output: Hello
let result2 = identity<number>(42);
console.log(result2); // Output: 42
- 泛型类
class Queue<T> {
private elements: T[] = [];
enqueue(element: T): void {
this.elements.push(element);
}
dequeue(): T | undefined {
return this.elements.shift();
}
}
// 使用泛型类
const numberQueue = new Queue<number>();
numberQueue.enqueue(1);
numberQueue.enqueue(2);
numberQueue.enqueue(3);
console.log(numberQueue.dequeue()); // Output: 1
const stringQueue = new Queue<string>();
stringQueue.enqueue('Hello');
stringQueue.enqueue('World');
console.log(stringQueue.dequeue()); // Output: Hello
- 泛型接口
//可定义接口中可修改的类型
interface Pair<T, U> {
first: T;
second: U;
}
// 使用泛型接口
const pair: Pair<string, number> = {
first: 'one',
second: 1,
};
console.log(pair); // Output: { first: 'one', second: 1 }
- 泛型的常见陷阱
使用泛型类型参数作为具体类型:在泛型函数或类内部,类型参数被视为变量,不能直接用作具体类型。例如,以下代码是错误的:
```ts
function identity<T>(arg: T): T {
let variable: T; // Error: Type parameter 'T' cannot be used as an individual type
// ...
}
```
如果需要在函数或类内部使用泛型类型参数作为具体类型,可以使用类型注解或类型别名来实现,如下:
可以使用类型别名来为泛型类型参数创建一个具名类型,然后在函数或类内部使用该类型别名作为具体类型。这样,您就可以在内部使用泛型类型参数作为具体类型。例如:
type GenericType<T> = T;
function identity<T>(arg: T): T {
let variable: GenericType<T>;
// ...
return variable;
}
接口继承(Interface Inheritance)
接口继承允许您从一个或多个接口继承属性和方法,并将它们组合成一个新的接口。这可以使您的代码更加模块化和可重用。 在TypeScript中,接口继承指的是一个接口可以从一个或多个接口继承属性和方法,并将它们组合成一个新的接口。这使得您可以将多个接口的属性和方法组合在一起,以创建一个更具体的接口。
接口继承使用extends关键字来指定要继承的接口。例如,假设您有以下两个接口:
interface Person {
name: string;
age: number;
}
interface Employee {
id: number;
salary: number;
}
可以使用接口继承来创建一个新的接口,该接口继承自这两个接口:
interface EmployeeInfo extends Person, Employee {
department: string;
}
在上面的示例中,EmployeeInfo接口继承了Person和Employee接口,并添加了一个新的department属性。现在,EmployeeInfo接口包含了所有三个接口的属性,这使得您可以更方便地定义一个包含所有员工信息的接口。
类型别名(Type Aliases)
类型别名允许您为任何类型创建一个别名,这可以使您的代码更加清晰、易于阅读和维护。
在TypeScript中,类型别名允许为任何类型创建一个别名。类型别名使用type关键字来定义,例如:
type Age = number;
type Person = {
name: string;
age: Age;
};
在上面的示例中,我们使用type关键字来定义了两个类型别名:Age和Person。Age别名将number类型定义为年龄,而Person别名是一个具有name和age属性的对象。现在,我们可以使用这些别名来定义变量和函数参数,例如:
function printPersonInfo(person: Person) {
console.log(`Name: ${person.name}, Age: ${person.age}`);
}
const john: Person = {
name: 'John',
age: 30
};
printPersonInfo(john);
在上面的示例中,我们使用Person别名定义了一个函数参数和一个对象变量。这使得代码更加清晰和易于阅读,因为我们可以使用Person别名来描述一个具有特定属性的对象类型。
类型别名还允许您定义复杂的类型,例如联合类型和交叉类型。例如,以下是一个使用类型别名定义联合类型的示例:
type Result = { success: true, value: number } | { success: false, error: string };
在上面的示例中,我们使用type关键字定义了一个Result别名,它是一个联合类型,可以是成功的对象(包含success和value属性)或失败的对象(包含success和error属性)。
总之,类型别名是TypeScript中非常有用的一种功能,它允许您为任何类型创建一个别名,使得代码更加清晰、易于阅读和维护。它们还可以用于定义复杂的类型,例如联合类型和交叉类型。
类型推断(Type Inference)
TypeScript的类型推断功能可以自动推断变量的类型,这可以使您的代码更加简洁和易于编写。
枚举(Enums)
枚举是一种特殊的类型,它允许您为一组相关的常量赋予一个名称。这可以使您的代码更加可读和易于理解。
高级类型(Advanced Types)
TypeScript提供了各种高级类型,例如交叉类型、联合类型、条件类型等,可以让您更加灵活地处理复杂的数据结构和逻辑。 TypeScript中的高级类型包括联合类型、交叉类型、类型别名、泛型、映射类型、条件类型、索引类型、可选属性和部分属性等。下面对每个高级类型进行简要介绍:
联合类型(Union Types)
联合类型表示一个变量可以是多种类型中的一种。例如,string | number表示一个变量可以是字符串或数字类型。
常用属性:|(联合类型操作符)、typeof(获取变量的类型)、extends(约束泛型类型的类型参数)
交叉类型(Intersection Types)
交叉类型表示一个变量包含多种类型的属性。例如,A & B表示一个变量包含类型A和类型B的属性。
常用属性:&(交叉类型操作符)
类型别名(Type Aliases)
类型别名允许您为任何类型创建一个别名。它们可以使您的代码更加清晰、易于阅读和维护。
常用属性:type(定义类型别名关键字)
泛型(Generics)
泛型允许您在定义函数、类和接口时使用类型参数。这使得您可以编写更通用和可重用的代码。
常用属性:<T>(定义类型参数)、extends(约束泛型类型的类型参数)
映射类型(Mapped Types)
映射类型允许您从现有类型中创建新类型。例如,您可以使用映射类型将所有属性变为可选属性。
常用属性:{ [P in K]: T }(将现有类型的属性映射为新的属性)、Partial<T>(将所有属性变为可选属性)
映射类型是TypeScript中的一种高级类型,它允许您从现有类型中创建新类型。映射类型可以将现有类型的属性进行转换、过滤或批量修改,从而生成一个新的类型。在TypeScript中,映射类型是通过使用索引类型和条件类型来实现的。
映射类型可以用以下语法来定义,下列是一些常见的用法:
type NewType = { [P in keyof OldType]: NewPropertyType };
在上面的语法中,OldType是要进行转换的现有类型,NewPropertyType是新的属性类型,NewType是生成的新类型。
例如,考虑以下接口:
interface Person {
name: string;
age: number;
address: string;
}
- 可选,现在,如果想将
Person接口中的所有属性都变为可选属性,可以使用映射类型来实现:
type PartialPerson = { [P in keyof Person]?: Person[P] };
在上面的示例中,PartialPerson是一个新的类型,它是通过将Person接口中的所有属性转换为可选属性来生成的。在映射类型中,[P in keyof Person]表示将Person接口中的所有属性进行映射,?表示将它们变为可选属性。
除了上述提到的将属性变为可选属性之外,还可以进行其他类型转换和操作。下面是一些映射类型的其他常见用法:
-
只读,将属性变为只读属性:
type ReadonlyPerson = { readonly [P in keyof Person]: Person[P] };在上面的示例中,使用
readonly关键字将Person接口中的所有属性转换为只读属性。 -
移除,从现有类型中移除某些属性:
type OmitPerson = Omit<Person, 'address'>;上面的示例中,使用
Omit类型工具从Person接口中移除了名为'address'的属性,生成了一个新的类型。 -
新增,添加新的属性:
type ExtendedPerson = Person & { gender: string };在上面的示例中,通过使用交叉类型操作符
&,将Person接口与一个包含新属性'gender'的对象类型进行合并,生成了一个包含新属性的扩展类型。 -
修改,根据现有属性生成新的属性:
type PersonWithUpperCaseName = { [P in keyof Person as `upper_${string & P}`]: string };上面的示例中,使用
as关键字和模板字符串,将Person接口中的所有属性进行映射,并生成一个带有前缀'upper_'的新属性。
映射类型还可以进行其他类型转换,例如将所有属性变为只读属性、移除某些属性、添加新的属性等。通过使用映射类型,您可以轻松地批量操作类型并生成新的类型,这使得代码更加通用和可维护。
条件类型(Conditional Types)
条件类型允许您在类型上进行条件分支。例如,您可以使用条件类型来确定一个变量是否为可选属性。
常用属性:T extends U ? X : Y(根据类型T是否满足类型U来确定返回类型)
索引类型(Index Types)
索引类型允许您使用字符串或数字类型的索引来访问对象的属性。例如,keyof T表示对象T的属性名称的联合类型。
常用属性:keyof T(获取对象T的属性名称的联合类型)、T[K](获取对象T中属性K的类型)
一个常见的应用是使用索引类型来实现类型安全的映射或转换操作。让我们通过一个示例来说明。
假设我们有一个名为User的接口,它包含用户的一些基本信息:
interface User {
id: number;
name: string;
email: string;
age: number;
}
现在,我们想要创建一个新的类型UserDTO,它是User类型的一个子集,只包含部分属性,并且属性名以dto_作为前缀。我们可以使用索引类型和映射类型来实现这个转换:
type UserDTO = {
[P in keyof User as `dto_${string & P}`]: User[P];
};
在上面的例子中,我们使用了映射类型的语法:[P in keyof User as dto_${string & P}]。这表示我们遍历User接口中的每个属性P,并生成一个新的属性名,前缀为dto_,然后将其与相应的属性值User[P]进行映射。
现在,我们可以创建一个User对象,并将其转换为UserDTO类型
const user: User = {
id: 1,
name: 'John Doe',
email: 'john@example.com',
age: 30,
};
const userDTO: UserDTO = {
dto_id: user.id,
dto_name: user.name,
dto_email: user.email,
dto_age: user.age,
};
可选属性(Optional Properties)
可选属性允许您在定义接口时指定某些属性为可选属性。这使得您可以定义一些属性是可选的,而不是必需的。
常用属性:?(指定属性为可选属性操作符)
部分属性(Partial Properties)
部分属性允许您在定义接口时将所有属性变为可选属性。这使得您可以定义一个对象,该对象可能只包含一部分属性。
常用属性:Partial<T>(将所有属性变为可选属性),意思就是我使用Partial然后在对象里输入我需要的person里的属性就可以生成只有这些属性的接口了。
eg:
interface Person {
name: string;
age: number;
address: string;
}
const partialPerson: Partial<Person> = {
name: 'John',
age: 30,
};
装饰器(Decorators)
装饰器是一种特殊的语法,它允许您在类、方法、属性等上面添加元数据和行为。这可以使您的代码更加模块化和可扩展。(个人感觉有点像对数据的备注信息...)