TypeScript 高级类型
TypeScript 提供了强大的类型操作能力,可以创建灵活且类型安全的代码。
联合类型
联合类型表示一个值可以是多种类型之一。
// 基本联合类型
type ID = string | number;
let userId: ID;
userId = "abc123"; // OK
userId = 123; // OK
// userId = true; // 错误
// 字符串字面量联合
type Status = "pending" | "approved" | "rejected";
type Role = "admin" | "user" | "guest";
function updateStatus(status: Status): void {
console.log(`Status updated to: ${status}`);
}
updateStatus("approved"); // OK
// updateStatus("unknown"); // 错误
类型收窄
function process(value: string | number): string {
// 类型守卫 - typeof
if (typeof value === "string") {
// 这里 value 是 string 类型
return value.toUpperCase();
} else {
// 这里 value 是 number 类型
return value.toFixed(2);
}
}
// 数组方法中的类型收窄
function getFirst(value: string | string[] | null): string {
if (value === null) {
return "empty";
}
if (Array.isArray(value)) {
return value[0] || "empty";
}
return value;
}
交叉类型
交叉类型将多个类型合并为一个类型。
interface Name {
name: string;
}
interface Age {
age: number;
}
type Person = Name & Age;
const person: Person = {
name: "张三",
age: 25
};
// 合并多个类型
interface Employee {
employeeId: string;
}
interface Manager {
teamSize: number;
}
type ManagerEmployee = Employee & Manager & Person;
const manager: ManagerEmployee = {
name: "李四",
age: 30,
employeeId: "E001",
teamSize: 5
};
合并冲突
interface A {
name: string;
value: number;
}
interface B {
name: string;
value: string; // 与 A 冲突
}
type C = A & B;
// value 类型变为 never,因为 number & string 没有交集
const c: C = {
name: "test",
// value 无法赋任何值
};
类型守卫
typeof 类型守卫
function printValue(value: string | number): void {
if (typeof value === "string") {
console.log(`String: ${value.toUpperCase()}`);
} else {
console.log(`Number: ${value.toFixed(2)}`);
}
}
instanceof 类型守卫
class Dog {
bark(): void {
console.log("汪汪!");
}
}
class Cat {
meow(): void {
console.log("喵喵!");
}
}
function makeSound(animal: Dog | Cat): void {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
in 操作符
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
function move(animal: Bird | Fish): void {
if ("fly" in animal) {
animal.fly();
} else {
animal.swim();
}
}
自定义类型守卫
interface User {
name: string;
email: string;
}
interface Admin extends User {
permissions: string[];
}
// 自定义类型守卫函数
function isAdmin(user: User | Admin): user is Admin {
return "permissions" in user;
}
function checkAccess(user: User | Admin): void {
if (isAdmin(user)) {
// user 这里是 Admin 类型
console.log(`Admin with ${user.permissions.length} permissions`);
} else {
// user 这里是 User 类型
console.log(`Regular user: ${user.name}`);
}
}
解释:user is Admin 是类型谓词,告诉 TypeScript 在函数返回 true 时,参数的类型是 Admin。
类型断言
基本类型断言
let value: unknown = "Hello World";
// 使用 as 语法
let length1: number = (value as string).length;
// 使用尖括号语法(不适用于 JSX)
let length2: number = (<string>value).length;
非空断言
// 非空断言操作符 !
function getElement(id: string): HTMLElement {
const element = document.getElementById(id);
// 断言 element 不为 null
return element!;
}
双重断言
// 通常不推荐,但在某些情况下有用
let value: string = "hello";
// 错误:string 和 number 不兼容
// let num: number = value as number;
// 双重断言绕过检查
let num: number = value as unknown as number;
映射类型
基本映射类型
// 将所有属性变为只读
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// 将所有属性变为可选
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface User {
name: string;
age: number;
}
type ReadonlyUser = Readonly<User>;
// { readonly name: string; readonly age: number; }
type PartialUser = Partial<User>;
// { name?: string; age?: number; }
映射修饰符
// 添加 readonly 和 可选修饰符
type MyPartial<T> = {
+readonly [P in keyof T]?: T[P];
};
// 移除 readonly 和 可选修饰符
type Required<T> = {
-readonly [P in keyof T]-?: T[P];
};
键重映射
// 重命名属性
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface Person {
name: string;
age: number;
}
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number; }
// 过滤属性
type OnlyStrings<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
type StringProps = OnlyStrings<{ a: string; b: number; c: string }>;
// { a: string; c: string; }
条件类型
基本语法
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
type C = IsString<"hello">; // true
条件类型约束
type Message<T extends { message: string }> = T["message"];
type Result = Message<{ message: "hello" }>; // "hello"
分布式条件类型
当条件类型作用于联合类型时,会自动分布到每个成员:
type ToArray<T> = T extends any ? T[] : never;
type Result = ToArray<string | number>;
// string[] | number[]
infer 关键字
infer 用于在条件类型中推断类型:
// 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function greet(): string {
return "hello";
}
type GreetReturn = ReturnType<typeof greet>; // string
// 提取函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
type GreetParams = Parameters<(a: string, b: number) => void>;
// [string, number]
// 提取 Promise 值类型
type Awaited<T> = T extends Promise<infer U> ? U : T;
type P = Awaited<Promise<string>>; // string
实际应用示例
// 提取数组元素类型
type ElementType<T> = T extends (infer E)[] ? E : never;
type Item = ElementType<string[]>; // string
// 提取对象属性类型
type PropertyType<T, K extends keyof T> = T[K];
// 深层 Required
type DeepRequired<T> = {
[K in keyof T]-?: T[K] extends object
? DeepRequired<T[K]>
: T[K];
};
// 深层 Readonly
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object
? DeepReadonly<T[K]>
: T[K];
};
模板字面量类型
基本用法
type Greeting = `hello ${string}`;
let message: Greeting = "hello world"; // OK
let message2: Greeting = "hello there"; // OK
// let message3: Greeting = "hi world"; // 错误
// 结合联合类型
type Color = "red" | "blue" | "green";
type Size = "small" | "medium" | "large";
type ColorSize = `${Color}-${Size}`;
// "red-small" | "red-medium" | "red-large" | "blue-small" | ...
// 字符串工具类型
type UppercaseName = Uppercase<"hello">; // "HELLO"
type LowercaseName = Lowercase<"HELLO">; // "hello"
type Capitalized = Capitalize<"hello">; // "Hello"
type Uncapitalized = Uncapitalize<"Hello">; // "hello"
实际应用
// 创建 getter/setter 类型
type GetterName<T extends string> = `get${Capitalize<T>}`;
type SetterName<T extends string> = `set${Capitalize<T>}`;
type Getters<T> = {
[K in keyof T as GetterName<string & K>]: () => T[K];
};
type Setters<T> = {
[K in keyof T as SetterName<string & K>]: (value: T[K]) => void;
};
interface State {
count: number;
name: string;
}
type StateGetters = Getters<State>;
// { getCount: () => number; getName: () => string; }
type StateSetters = Setters<State>;
// { setCount: (value: number) => void; setName: (value: string) => void; }
索引访问类型
属性访问
interface User {
name: string;
age: number;
address: {
city: string;
country: string;
};
}
type UserName = User["name"]; // string
type UserAge = User["age"]; // number
type UserAddress = User["address"]; // { city: string; country: string; }
type UserCity = User["address"]["city"]; // string
动态属性访问
type UserKeys = keyof User; // "name" | "age" | "address"
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user: User = {
name: "张三",
age: 25,
address: { city: "北京", country: "中国" }
};
const name = getProperty(user, "name"); // string
const city = getProperty(user, "address"); // { city: string; country: string; }
数组元素类型
const colors = ["red", "green", "blue"] as const;
type Color = typeof colors[number]; // "red" | "green" | "blue"
type ColorKeys = typeof colors; // readonly ["red", "green", "blue"]
小结
本章我们学习了 TypeScript 高级类型:
- 联合类型和交叉类型
- 类型守卫和类型收窄
- 类型断言
- 映射类型
- 条件类型和 infer 关键字
- 模板字面量类型
- 索引访问类型
练习
- 实现一个
DeepPartial<T>类型,递归地将所有属性变为可选 - 使用条件类型实现
IsArray<T>类型 - 创建一个映射类型,为对象的所有属性生成对应的 setter 方法类型
- 实现一个
ExtractEventNames<T>类型,提取所有以 "on" 开头的属性名