高级类型
# 专栏笔记总结大全
# 目录介绍
- 01.基础类型扩展
- 1.1 联合类型
- 1.2 交叉类型
- 1.3 字面量类型
- 1.4 模板字面量类型
- 1.5 对象类型
- 1.6 函数类型
- 1.7 泛型类型
- 02.类型操作
- 2.1 type类型别名
- 2.2 映射类型
- 2.3 条件类型
- 2.4 索引类型
- 2.5 递归类型
- 2.6 与interface区别
- 03.类型安全
- 3.1 类型断言
- 3.2 类型保护
- 3.3 类型推断
- 3.4 类型兼容性
- 3.5 非空断言
- 04.类型工具
- 4.1 Partial
- 4.2 Required
- 4.3 Readonly
- 4.4 Record<K, T>
- 4.5 Pick<T, K>
- 4.6 Omit<T, K>
- 4.1 Partial
- 05.declare
- 5.1 声明变量
- 5.2 声明全局模块
- 5.3 声明全局命名空间
- 5.4 声明类型
- 5.5 声明类
- 5.6 声明模块扩展
- 5.7 声明枚举
- 5.8 声明函数重载
- 06.d.ts类型声明
- 6.1 .ts文件介绍
- 6.2 .d.ts文件
- 6.3 两者区别
- 6.4 实际应用
- 6.5 自动生成.d.ts
TypeScript 的高级类型是其类型系统的强大扩展,提供了更灵活和复杂的类型操作能力。
这些高级类型可以帮助开发者更好地描述和约束代码的行为,提升代码的安全性和可维护性。以下是 TypeScript 中常见的高级类型及其用法:
# 01.基础类型扩展
# 1.1 联合类型
联合类型 (Union Types),表示一个值可以是多种类型之一。
let value: string | number;
value = "hello"; // 正确
value = 10; // 正确
value = true; // 错误
// 技术点:联合类型(Union Types)+ 类型别名(Type Alias)
type HeadersInit = [string, string][] | Record<string, string>;
2
3
4
5
6
7
# 1.2 交叉类型
交叉类型 (Intersection Types),将多个类型合并为一个类型,新类型包含所有类型的特性。
interface A {
a: number;
}
interface B {
b: string;
}
type C = A & B;
let obj: C = { a: 1, b: "hello" };
2
3
4
5
6
7
8
# 1.3 字面量类型
字面量类型 (Literal Types),表示一个具体的值,通常与联合类型一起使用。
let direction: "left" | "right" | "up" | "down";
direction = "left"; // 正确
direction = "side"; // 错误
2
3
# 1.4 模板字面量类型
模板字面量类型 (Template Literal Types), 基于字符串字面量创建新类型。
type EventName = "click" | "hover";
type HandlerName = `on${Capitalize<EventName>}`;
let handler: HandlerName = "onClick"; // 正确
handler = "onHover"; // 正确
handler = "onScroll"; // 错误
2
3
4
5
# 1.5 对象类型
type 可以用来定义对象类型:
type User = {
id: number;
name: string;
email: string;
};
const user: User = {
id: 1,
name: "Alice",
email: "alice@example.com",
};
2
3
4
5
6
7
8
9
10
11
# 1.6 函数类型
type 可以用来定义函数类型:
type AddFunction = (a: number, b: number) => number;
const add: AddFunction = (a, b) => {
return a + b;
};
2
3
4
5
# 1.7 泛型类型
允许我们编写可重用的、类型安全的代码。通过泛型,可以创建适用于多种类型的组件、函数或类,而无需重复编写相同的逻辑。
泛型允许我们在定义函数、类或接口时使用类型参数,这些类型参数在使用时可以被具体的类型替换。泛型的核心思想是参数化类型。
1.泛型函数:可以处理多种类型的参数,而无需为每种类型编写单独的函数。
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("Hello"); // 类型为 string
let output2 = identity<number>(42); // 类型为 number
2
3
4
5
6
2.泛型类:可以定义适用于多种类型的类。
class Box<T> {
private value: T;
constructor(value: T) { this.value = value; }
getValue(): T { return this.value; }
}
let box1 = new Box<string>("Hello");
let box2 = new Box<number>(42);
2
3
4
5
6
7
8
3.泛型接口:可以定义适用于多种类型的接口。
interface Pair<T, U> {
first: T;
second: U;
}
let pair: Pair<string, number> = { first: "Age", second: 30 };
2
3
4
5
6
# 02.类型操作
# 2.1 type类型别名
type 是一个关键字,用于定义 类型别名 或 自定义类型。它允许你为现有的类型创建一个新的名称,或者将多个类型组合成一个新的类型。
类型别名 (Type Aliases),为类型创建一个别名,方便复用。
type StringOrNumber = string | number;
let value: StringOrNumber;
value = "hello"; // 正确
value = 10; // 正确
2
3
4
# 2.2 映射类型
映射类型 (Mapped Types)。基于现有类型创建新类型,通常用于批量修改属性。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = Readonly<Person>;
let person: ReadonlyPerson = { name: "Alice", age: 30 };
person.name = "Bob"; // 错误,属性是只读的
2
3
4
5
6
7
8
9
10
# 2.3 条件类型
条件类型 (Conditional Types),根据条件选择类型。
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; // true
type B = IsString<10>; // false
2
3
# 2.4 索引类型
通过索引访问类型的属性。
type Person = { name: string; age: number };
type Name = Person["name"]; // string
2
# 2.5 递归类型
类型可以引用自身,用于定义递归结构。
type TreeNode = { value: number; left?: TreeNode; right?: TreeNode };
# 2.6 与interface区别
type 和 interface 都可以用来定义类型,但它们有一些区别:
type:
- 可以定义任何类型(基本类型、联合类型、交叉类型等)。
- 不支持扩展(但可以通过交叉类型实现类似功能)。
- 更适合定义复杂的类型。
interface:
- 主要用于定义对象类型。
- 支持扩展(通过 extends 关键字)。
- 更适合定义对象的形状。
# 03.类型安全
# 3.1 类型断言
1.普通类型断言。用于显式地告诉 TypeScript 某个值的类型,通常用于覆盖 TypeScript 的类型推断。
类型断言 (Type Assertion),告诉 TypeScript 一个值的具体类型。
let value: any = "hello";
let length: number = (value as string).length;
2
2.as const 断言。as const 是一种特殊的类型断言,用于将值标记为不可变(只读),并将其类型推断为最具体的字面量类型,而不是更宽泛的类型。
const FOTMAT = ["debug", "info", "warn", "error"] as const;
在这个例子中:
- as const 将 FOTMAT 的类型推断为 readonly ["debug", "info", "warn", "error"],而不是 string[]。
- 数组中的每个元素都被推断为具体的字面量类型(如 "debug" 而不是 string)。
- 数组本身被标记为只读,无法修改。
# 3.2 类型保护
在运行时检查类型,缩小类型范围。
if (typeof value === "string") {
console.log(value.toUpperCase());
}
2
3
类型守卫 (Type Guards),通过条件语句缩小类型范围。
function isString(value: any): value is string {
return typeof value === "string";
}
let value: string | number;
if (isString(value)) {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
2
3
4
5
6
7
8
9
10
# 3.3 类型推断
类型推断 (Infer),在条件类型中推断类型。
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function foo(): number {
return 42;
}
type FooReturnType = ReturnType<typeof foo>; // number
2
3
4
5
# 3.4 类型兼容性
类型兼容性 (Type Compatibility),TypeScript 使用结构化类型系统,只要结构匹配,类型就是兼容的。
interface Named {
name: string;
}
class Person {
name: string;
}
let p: Named;
p = new Person(); // 正确,因为结构匹配
2
3
4
5
6
7
8
# 3.5 非空断言
非空断言 (Non-null Assertion),明确告诉 TypeScript 一个值不是 null 或 undefined。
let value: string | null = "hello";
console.log(value!.toUpperCase()); // 非空断言
2
# 04.类型工具
类型工具 (Utility Types),TypeScript 提供了一些内置的工具类型,用于简化类型操作。
# 4.1 Partial
将类型 T 的所有属性变为可选。
interface Person {
name: string;
age: number;
}
type PartialPerson = Partial<Person>;
let person: PartialPerson = { name: "Alice" }; // age 是可选的
2
3
4
5
6
# 4.2 Required
将类型 T 的所有属性变为必选。
type RequiredPerson = Required<PartialPerson>;
let person: RequiredPerson = { name: "Alice", age: 30 };
2
# 4.3 Readonly
将类型 T 的所有属性变为只读。
type ReadonlyPerson = Readonly<Person>;
let person: ReadonlyPerson = { name: "Alice", age: 30 };
person.name = "Bob"; // 错误,属性是只读的
2
3
# 4.4 Record<K, T>
创建一个对象类型,其属性键为 K,值为 T。
type PersonRecord = Record<"name" | "age", string>;
let person: PersonRecord = { name: "Alice", age: "30" };
2
# 4.5 Pick<T, K>
从类型 T 中选择部分属性 K。
type NameOnly = Pick<Person, "name">;
let person: NameOnly = { name: "Alice" };
2
# 4.6 Omit<T, K>
从类型 T 中排除部分属性 K。
type AgeOnly = Omit<Person, "name">;
let person: AgeOnly = { age: 30 };
2
# 05.declare
在 TypeScript 中,declare 是一个关键字,用于告诉编译器某些变量、函数、类或模块的类型信息,而无需提供具体的实现。
declare 关键字在 TypeScript 中主要用于以下场景:
- 声明全局变量、函数、类或模块的类型。
- 为第三方 JavaScript 库提供类型支持。
- 扩展现有模块的类型。
- 声明类型别名、接口或枚举。
通过 declare,TypeScript 可以在不提供具体实现的情况下,对代码进行类型检查和智能提示,从而提高开发效率和代码质量。
# 5.1 声明变量
声明全局变量,当使用全局变量(例如在浏览器环境中定义的 window 对象或第三方库)时,可以使用 declare 声明其类型。
declare const VERSION: string;
declare function log(message: string): void;
console.log(VERSION); // 使用全局变量
log("Hello, TypeScript!"); // 调用全局函数
2
3
4
5
声明变量类型,使用 declare 声明变量的类型。
declare let myVar: number;
myVar = 10;
console.log(myVar);
2
3
# 5.2 声明全局模块
声明全局模块:为第三方 JavaScript 库(如 jQuery、Lodash)提供类型支持。
declare module "jquery" {
function $(selector: string): any;
export = $;
}
// 使用 jQuery
$("button").click(() => {
console.log("Button clicked!");
});
2
3
4
5
6
7
8
9
# 5.3 声明全局命名空间
使用 declare namespace 声明全局命名空间。
declare namespace MyLibrary {
function doSomething(): void;
}
MyLibrary.doSomething();
2
3
4
5
# 5.4 声明类型
使用 declare 声明类型别名或接口。
declare type Point = {
x: number;
y: number;
};
declare interface Person {
name: string;
age: number;
}
2
3
4
5
6
7
8
9
# 5.5 声明类
使用 declare 声明类,但不提供具体实现。
declare class Animal {
name: string;
constructor(name: string);
speak(): void;
}
const dog = new Animal("Dog");
dog.speak();
2
3
4
5
6
7
8
# 5.6 声明模块扩展
为现有模块添加额外的类型声明。
import "express";
declare module "express" {
interface Request {
user?: { id: string };
}
}
2
3
4
5
6
7
# 5.7 声明枚举
使用 declare 声明枚举类型。
declare enum Direction {
Up,
Down,
Left,
Right,
}
const dir: Direction = Direction.Up;
2
3
4
5
6
7
8
# 5.8 声明函数重载
使用 declare 声明函数重载。
declare function add(a: number, b: number): number;
declare function add(a: string, b: string): string;
console.log(add(1, 2)); // 3
console.log(add("Hello, ", "TypeScript!")); // Hello, TypeScript!
2
3
4
5
# 06.d.ts类型声明
.d.ts 和 .ts 是 TypeScript 中两种不同的文件类型,它们的主要区别在于用途和内容。
.ts文件:用于编写可执行的 TypeScript 代码,会被编译为.js文件。.d.ts文件:用于声明类型信息,不包含可执行代码,主要用于为 JavaScript 库提供类型支持或声明全局类型。- 在实际开发中,
.ts文件用于实现功能,而.d.ts文件用于增强类型检查和代码提示。
# 6.1 .ts文件介绍
1.用途
.ts 文件是标准的 TypeScript 文件,包含可执行的 TypeScript 代码。它可以包含变量、函数、类、接口等 TypeScript 代码,并且会被编译为 JavaScript 文件。
2.示例
// math.ts
export function add(a: number, b: number): number {
return a + b;
}
2
3
4
3.编译
.ts 文件会被 TypeScript 编译器(tsc)编译为 .js 文件。例如,math.ts 会被编译为 math.js。
# 6.2 .d.ts文件
1.用途
.d.ts 文件是 类型声明文件,用于描述 JavaScript 库或模块的类型信息。它不包含可执行代码,只包含类型声明(如接口、类型别名、函数签名等)。主要用于为 JavaScript 代码提供类型支持,或者在 TypeScript 项目中声明全局类型。
2.示例
// math.d.ts
declare function add(a: number, b: number): number;
2
3.使用场景
- 为 JavaScript 库提供类型支持:例如,为第三方 JavaScript 库(如 jQuery、Lodash)编写类型声明文件。
- 声明全局类型:在 TypeScript 项目中声明全局变量、模块或类型。
- 模块扩展:为现有模块添加额外的类型声明。
4.编译
.d.ts 文件不会被编译为 .js 文件,因为它只包含类型信息。
# 6.3 两者区别
| 特性 | .ts 文件 | .d.ts 文件 |
|---|---|---|
| 用途 | 包含可执行的 TypeScript 代码 | 包含类型声明,不包含可执行代码 |
| 编译结果 | 编译为 .js 文件 | 不编译,仅用于类型检查 |
| 内容 | 变量、函数、类、接口等 | 类型声明(接口、类型别名、函数签名等) |
| 使用场景 | 编写业务逻辑或功能代码 | 为 JavaScript 库提供类型支持或声明全局类型 |
# 6.4 实际应用
1.为 JavaScript 库提供类型支持
假设有一个 JavaScript 库 math.js:
// math.js
function add(a, b) {
return a + b;
}
module.exports = { add };
2
3
4
5
可以为其编写类型声明文件 math.d.ts:
// math.d.ts
declare module "math" {
export function add(a: number, b: number): number;
}
2
3
4
2.声明全局类型,在 TypeScript 项目中声明全局变量:
// globals.d.ts
declare const VERSION: string;
declare function log(message: string): void;
2
3
3.模块扩展,为现有模块添加额外的类型声明:
// express.d.ts
import "express";
declare module "express" {
interface Request {
user?: { id: string };
}
}
2
3
4
5
6
7
8
# 6.5 自动生成.d.ts
使用 TypeScript 编译器的 declaration 选项,可以自动为 .ts 文件生成 .d.ts 文件。
// tsconfig.json
{
"compilerOptions": {
"declaration": true
}
}
2
3
4
5
6
编译后,每个 .ts 文件会生成对应的 .d.ts 文件。