Typescript学习教程,从入门到精通,TypeScript 泛型与类型操作详解(二)(17)
TypeScript 泛型与类型操作详解(二)
本文将详细介绍 TypeScript 中的一些高级类型特性,包括条件类型、分布式条件类型、infer
关键字、内置工具类型、类型查询、类型断言、类型细化和类型守卫等。
1. 条件类型(Conditional Types)
1.1 定义
条件类型允许根据类型之间的关系在类型系统中进行条件判断。其语法类似于 JavaScript 中的三元运算符:
T extends U ? X : Y
- 解释: 如果类型
T
可以赋值给类型U
,则结果类型为X
,否则为Y
。
1.2 示例
type IsString<T> = T extends string ? true : false;type A = IsString<string>; // A 的类型为 true
type B = IsString<number>; // B 的类型为 false
2. 分布式条件类型(Distributive Conditional Types)
当条件类型应用于联合类型时,TypeScript 会将联合类型中的每个成员单独应用于条件类型,这种行为称为“分布式”。
2.1 示例
type ToArray<T> = T extends any ? T[] : never;type A = ToArray<string | number>; // A 的类型为 string[] | number[]
在这个例子中,ToArray
条件类型被应用于 string | number
,结果为 string[] | number[]
。
3. infer
关键字
infer
关键字用于在条件类型中推断类型。它通常用于提取类型的一部分,例如函数的返回类型或参数类型。
3.1 示例:提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;// 使用内置的 ReturnType
type Func = () => string;
type Result = ReturnType<Func>; // Result 的类型为 string
3.2 示例:提取数组元素类型
type ElementType<T> = T extends (infer E)[] ? E : T;type A = ElementType<string[]>; // A 的类型为 string
type B = ElementType<number>; // B 的类型为 number
4. 内置工具类型(Utility Types)
TypeScript 提供了一些内置的工具类型,简化常见的类型操作。以下是一些常用的工具类型及其示例。
4.1 Partial<T>
将类型 T
的所有属性设为可选。
interface User {id: number;name: string;age?: number;
}type PartialUser = Partial<User>;
// PartialUser 等同于:
// {
// id?: number;
// name?: string;
// age?: number;
// }
4.2 Required<T>
将类型 T
的所有属性设为必填。
type RequiredUser = Required<PartialUser>;
// RequiredUser 等同于:
// {
// id: number;
// name: string;
// age: number;
// }
4.3 Readonly<T>
将类型 T
的所有属性设为只读。
type ReadonlyUser = Readonly<User>;
// ReadonlyUser 等同于:
// {
// readonly id: number;
// readonly name: string;
// readonly age?: number;
// }
4.4 Record<K, T>
构造一个对象类型,其键为 K
,值为 T
。
type StringNumberMap = Record<string, number>;
// StringNumberMap 等同于:
// {
// [key: string]: number;
// }type UserRoles = Record<'admin' | 'user', boolean>;
// UserRoles 等同于:
// {
// admin: boolean;
// user: boolean;
// }
4.5 Pick<T, K>
从类型 T
中选取一组属性 K
构成新类型。
type PickUser = Pick<User, 'id' | 'name'>;
// PickUser 等同于:
// {
// id: number;
// name: string;
// }
4.6 Omit<T, K>
从类型 T
中排除一组属性 K
构成新类型。
type OmitUser = Omit<User, 'age'>;
// OmitUser 等同于:
// {
// id: number;
// name: string;
// }
4.7 Exclude<T, U>
从类型 T
中排除可赋值给 U
的部分。
type ExcludeString = Exclude<string | number | boolean, string | number>;
// ExcludeString 的类型为 boolean
4.8 Extract<T, U>
从类型 T
中提取可赋值给 U
的部分。
type ExtractString = Extract<string | number | boolean, string>;
// ExtractString 的类型为 string
4.9 NonNullable<T>
从类型 T
中排除 null
和 undefined
。
type NonNullableUser = NonNullable<User | null | undefined>;
// NonNullableUser 的类型为 User
4.10 Parameters<T>
提取函数类型 T
的参数类型组成的元组类型。
type Func = (a: string, b: number) => void;
type Params = Parameters<Func>;
// Params 的类型为 [string, number]
4.11 ConstructorParameters<T>
提取构造函数类型 T
的参数类型组成的元组类型。
type SomeClass = new (a: string, b: number) => void;
type ConstructorParams = ConstructorParameters<SomeClass>;
// ConstructorParams 的类型为 [string, number]
4.12 ReturnType<T>
提取函数类型 T
的返回类型。
type Func = () => string;
type Return = ReturnType<Func>;
// Return 的类型为 string
4.13 InstanceType<T>
提取构造函数类型 T
的实例类型。
type SomeClass = new () => { id: number };
type Instance = InstanceType<SomeClass>;
// Instance 的类型为 { id: number }
4.14 ThisParameterType<T>
提取函数类型 T
的 this
参数类型。
type FuncWithThis = (this: Date, x: string) => void;
type ThisParam = ThisParameterType<FuncWithThis>;
// ThisParam 的类型为 Date
4.15 OmitThisParameter<T>
移除函数类型 T
的 this
参数类型。
type FuncWithoutThis = OmitThisParameter<FuncWithThis>;
// FuncWithoutThis 的类型为 (x: string) => void
4.16 ThisType<T>
用于在对象字面量中指定 this
的上下文类型。
type ObjectWithThis = {id: number;getId(this: ObjectWithThis): number;
};const obj: ObjectWithThis = {id: 1,getId(this: ObjectWithThis) {return this.id;}
};
5. 类型查询(Type Queries)
类型查询使用 typeof
操作符来获取变量或属性的类型。
5.1 示例
let age = 25;
type AgeType = typeof age; // AgeType 的类型为 numberconst user = {id: 1,name: 'Alice'
};
type UserType = typeof user;
// UserType 的类型为 { id: number; name: string }
6. 类型断言(Type Assertions)
类型断言允许开发者手动指定一个值的类型,绕过 TypeScript 的类型检查。
6.1 <T>类型断言
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
6.2 as T
类型断言
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
6.3 类型断言的约束
类型断言不能违反类型层级。例如,不能将 number
断言为 string
。
6.4 const
类型断言
const
断言用于创建只读、字面量类型的值。
let x = "hello" as const;
// x 的类型为 "hello"let y = { id: 1, name: "Alice" } as const;
// y 的类型为 { readonly id: 1; readonly name: "Alice" }
6.5 !
类型断言
!
用于断言某个值不是 null
或 undefined
。
function getLength(s?: string) {return s!.length;
}
7. 类型细化(Type Narrowing)
类型细化是通过代码逻辑来缩小变量的类型范围,常用的方法包括类型守卫、赋值语句分析、控制流分析等。
7.1 类型守卫(Type Guards)
7.1.1 typeof
类型守卫
function printId(id: string | number) {if (typeof id === "string") {console.log(id.toUpperCase());} else {console.log(id.toFixed(2));}
}
7.1.2 instanceof
类型守卫
function printDate(date: Date | string) {if (date instanceof Date) {console.log(date.toUTCString());} else {console.log(date.toUpperCase());}
}
7.1.3 自定义类型守卫
interface Cat {meow(): void;
}interface Dog {bark(): void;
}function isCat(animal: Cat | Dog): animal is Cat {return (animal as Cat).meow !== undefined;
}function speak(animal: Cat | Dog) {if (isCat(animal)) {animal.meow();} else {animal.bark();}
}
7.2 赋值语句分析
通过赋值语句,TypeScript 可以推断出变量的更具体类型。
let x: string | number;
x = "hello";
// x 的类型被细化为 stringx = 123;
// x 的类型被细化为 number
7.3 基于控制流的类型分析
TypeScript 会根据控制流(如 if
、else
、return
等)来推断变量的类型。
function example(x: string | number) {if (typeof x === "string") {// x 的类型为 stringconsole.log(x.toUpperCase());} else {// x 的类型为 numberconsole.log(x.toFixed(2));}
}
7.4 断言函数(Assertion Functions)
断言函数用于在运行时验证类型,并在验证失败时抛出错误。
function assertIsString(val: any): asserts val is string {if (typeof val !== "string") {throw new Error("Value is not a string");}
}function printString(s: string | number) {assertIsString(s);console.log(s.toUpperCase());
}
8. 案例代码
8.1 使用 Partial
和 Pick
创建部分属性对象
interface User {id: number;name: string;age: number;email: string;
}function updateUser(user: User, updates: Partial<Pick<User, 'name' | 'email'>>) {return { ...user, ...updates };
}const alice: User = { id: 1, name: "Alice", age: 30, email: "alice@example.com" };
const updatedAlice = updateUser(alice, { name: "Alicia", email: "alicia@example.com" });
// updatedAlice 的类型为 User
8.2 使用 Exclude
和 Extract
进行类型操作
type Mixed = string | number | boolean | string[];type NonString = Exclude<Mixed, string>; // NonString 的类型为 number | boolean | string[]
type OnlyString = Extract<Mixed, string>; // OnlyString 的类型为 stringtype NonEmptyString = Exclude<OnlyString, ''>; // NonEmptyString 的类型为 string
8.3 使用 ReturnType
获取函数返回类型
type GreetFunc = (name: string) => string;type GreetReturn = ReturnType<GreetFunc>; // GreetReturn 的类型为 stringfunction greet(name: string): GreetReturn {return `Hello, ${name}!`;
}
8.4 使用 Parameters
获取函数参数类型
type SumFunc = (a: number, b: number) => number;type SumParams = Parameters<SumFunc>; // SumParams 的类型为 [number, number]function sum(a: number, b: number): number {return a + b;
}const params: SumParams = [1, 2];
8.5 使用 ThisParameterType
和 OmitThisParameter
处理 this
参数
type FuncWithThis = (this: Date, x: string) => void;type ThisParam = ThisParameterType<FuncWithThis>; // ThisParam 的类型为 Datetype FuncWithoutThis = OmitThisParameter<FuncWithThis>; // FuncWithoutThis 的类型为 (x: string) => voidfunction example(this: ThisParam, x: string) {console.log(this.toUTCString(), x);
}const date = new Date();
example.call(date, "hello");
8.6 使用 ThisType
在对象字面量中指定 this
类型
type ObjectWithThis = {id: number;getId(this: ObjectWithThis): number;
};const obj: ObjectWithThis = {id: 1,getId(this: ObjectWithThis) {return this.id;}
};console.log(obj.getId());
8.7 使用 is
进行自定义类型守卫
interface Cat {meow(): void;
}interface Dog {bark(): void;
}function isCat(animal: Cat | Dog): animal is Cat {return (animal as Cat).meow !== undefined;
}function speak(animal: Cat | Dog) {if (isCat(animal)) {animal.meow();} else {animal.bark();}
}const cat: Cat = { meow: () => console.log("Meow!") };
const dog: Dog = { bark: () => console.log("Woof!") };speak(cat); // 输出: Meow!
speak(dog); // 输出: Woof!
8.8 使用 const
断言创建只读对象
const config = {apiUrl: "https://api.example.com",timeout: 3000
} as const;type ConfigType = typeof config;
// ConfigType 的类型为:
// {
// readonly apiUrl: "https://api.example.com";
// readonly timeout: 3000;
// }
8.9 使用 NonNullable
排除 null
和 undefined
type MaybeString = string | null | undefined;type DefiniteString = NonNullable<MaybeString>; // DefiniteString 的类型为 stringfunction getString(): MaybeString {return "Hello";
}const str: DefiniteString = getString()!;
8.10 使用 InstanceType
获取构造函数实例类型
class Person {constructor(public name: string) {}
}type PersonConstructor = typeof Person;type PersonInstance = InstanceType<PersonConstructor>; // PersonInstance 的类型为 Personconst person: PersonInstance = new Person("Alice");
总结
TypeScript 提供了丰富的类型操作工具,使得开发者能够在编译阶段进行更严格的类型检查和更灵活的类型操作。通过理解和应用这些高级类型特性,可以显著提升代码的可维护性和可靠性。