Typescript学习教程,从入门到精通,TypeScript 泛型与类型操作详解(二)(17)

TypeScript 泛型与类型操作详解(二)

本文将详细介绍 TypeScript 中的一些高级类型特性,包括条件类型、分布式条件类型、infer 关键字、内置工具类型、类型查询、类型断言、类型细化和类型守卫等。

1. 条件类型(Conditional Types)

1.1 定义

条件类型允许根据类型之间的关系在类型系统中进行条件判断。其语法类似于 JavaScript 中的三元运算符:

typescript 复制代码
T extends U ? X : Y
  • 解释 : 如果类型 T 可以赋值给类型 U,则结果类型为 X,否则为 Y

1.2 示例

typescript 复制代码
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 示例

typescript 复制代码
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 示例:提取函数返回类型

typescript 复制代码
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// 使用内置的 ReturnType
type Func = () => string;
type Result = ReturnType<Func>; // Result 的类型为 string

3.2 示例:提取数组元素类型

typescript 复制代码
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 的所有属性设为可选。

typescript 复制代码
interface User {
  id: number;
  name: string;
  age?: number;
}

type PartialUser = Partial<User>;
// PartialUser 等同于:
// {
//   id?: number;
//   name?: string;
//   age?: number;
// }

4.2 Required<T>

将类型 T 的所有属性设为必填。

typescript 复制代码
type RequiredUser = Required<PartialUser>;
// RequiredUser 等同于:
// {
//   id: number;
//   name: string;
//   age: number;
// }

4.3 Readonly<T>

将类型 T 的所有属性设为只读。

typescript 复制代码
type ReadonlyUser = Readonly<User>;
// ReadonlyUser 等同于:
// {
//   readonly id: number;
//   readonly name: string;
//   readonly age?: number;
// }

4.4 Record<K, T>

构造一个对象类型,其键为 K,值为 T

typescript 复制代码
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 构成新类型。

typescript 复制代码
type PickUser = Pick<User, 'id' | 'name'>;
// PickUser 等同于:
// {
//   id: number;
//   name: string;
// }

4.6 Omit<T, K>

从类型 T 中排除一组属性 K 构成新类型。

typescript 复制代码
type OmitUser = Omit<User, 'age'>;
// OmitUser 等同于:
// {
//   id: number;
//   name: string;
// }

4.7 Exclude<T, U>

从类型 T 中排除可赋值给 U 的部分。

typescript 复制代码
type ExcludeString = Exclude<string | number | boolean, string | number>;
// ExcludeString 的类型为 boolean

4.8 Extract<T, U>

从类型 T 中提取可赋值给 U 的部分。

typescript 复制代码
type ExtractString = Extract<string | number | boolean, string>;
// ExtractString 的类型为 string

4.9 NonNullable<T>

从类型 T 中排除 nullundefined

typescript 复制代码
type NonNullableUser = NonNullable<User | null | undefined>;
// NonNullableUser 的类型为 User

4.10 Parameters<T>

提取函数类型 T 的参数类型组成的元组类型。

typescript 复制代码
type Func = (a: string, b: number) => void;
type Params = Parameters<Func>;
// Params 的类型为 [string, number]

4.11 ConstructorParameters<T>

提取构造函数类型 T 的参数类型组成的元组类型。

typescript 复制代码
type SomeClass = new (a: string, b: number) => void;
type ConstructorParams = ConstructorParameters<SomeClass>;
// ConstructorParams 的类型为 [string, number]

4.12 ReturnType<T>

提取函数类型 T 的返回类型。

typescript 复制代码
type Func = () => string;
type Return = ReturnType<Func>;
// Return 的类型为 string

4.13 InstanceType<T>

提取构造函数类型 T 的实例类型。

typescript 复制代码
type SomeClass = new () => { id: number };
type Instance = InstanceType<SomeClass>;
// Instance 的类型为 { id: number }

4.14 ThisParameterType<T>

提取函数类型 Tthis 参数类型。

typescript 复制代码
type FuncWithThis = (this: Date, x: string) => void;
type ThisParam = ThisParameterType<FuncWithThis>;
// ThisParam 的类型为 Date

4.15 OmitThisParameter<T>

移除函数类型 Tthis 参数类型。

typescript 复制代码
type FuncWithoutThis = OmitThisParameter<FuncWithThis>;
// FuncWithoutThis 的类型为 (x: string) => void

4.16 ThisType<T>

用于在对象字面量中指定 this 的上下文类型。

typescript 复制代码
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 示例

typescript 复制代码
let age = 25;
type AgeType = typeof age; // AgeType 的类型为 number

const user = {
  id: 1,
  name: 'Alice'
};
type UserType = typeof user;
// UserType 的类型为 { id: number; name: string }

6. 类型断言(Type Assertions)

类型断言允许开发者手动指定一个值的类型,绕过 TypeScript 的类型检查。

6.1 <T>类型断言

typescript 复制代码
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

6.2 as T 类型断言

typescript 复制代码
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;

6.3 类型断言的约束

类型断言不能违反类型层级。例如,不能将 number 断言为 string

6.4 const 类型断言

const 断言用于创建只读、字面量类型的值。

typescript 复制代码
let x = "hello" as const;
// x 的类型为 "hello"

let y = { id: 1, name: "Alice" } as const;
// y 的类型为 { readonly id: 1; readonly name: "Alice" }

6.5 ! 类型断言

! 用于断言某个值不是 nullundefined

typescript 复制代码
function getLength(s?: string) {
  return s!.length;
}

7. 类型细化(Type Narrowing)

类型细化是通过代码逻辑来缩小变量的类型范围,常用的方法包括类型守卫、赋值语句分析、控制流分析等。

7.1 类型守卫(Type Guards)

7.1.1 typeof 类型守卫
typescript 复制代码
function printId(id: string | number) {
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id.toFixed(2));
  }
}
7.1.2 instanceof 类型守卫
typescript 复制代码
function printDate(date: Date | string) {
  if (date instanceof Date) {
    console.log(date.toUTCString());
  } else {
    console.log(date.toUpperCase());
  }
}
7.1.3 自定义类型守卫
typescript 复制代码
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 可以推断出变量的更具体类型。

typescript 复制代码
let x: string | number;
x = "hello";
// x 的类型被细化为 string

x = 123;
// x 的类型被细化为 number

7.3 基于控制流的类型分析

TypeScript 会根据控制流(如 ifelsereturn 等)来推断变量的类型。

typescript 复制代码
function example(x: string | number) {
  if (typeof x === "string") {
    // x 的类型为 string
    console.log(x.toUpperCase());
  } else {
    // x 的类型为 number
    console.log(x.toFixed(2));
  }
}

7.4 断言函数(Assertion Functions)

断言函数用于在运行时验证类型,并在验证失败时抛出错误。

typescript 复制代码
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 使用 PartialPick 创建部分属性对象

typescript 复制代码
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: "[email protected]" };
const updatedAlice = updateUser(alice, { name: "Alicia", email: "[email protected]" });
// updatedAlice 的类型为 User

8.2 使用 ExcludeExtract 进行类型操作

typescript 复制代码
type Mixed = string | number | boolean | string[];

type NonString = Exclude<Mixed, string>; // NonString 的类型为 number | boolean | string[]
type OnlyString = Extract<Mixed, string>;  // OnlyString 的类型为 string

type NonEmptyString = Exclude<OnlyString, ''>; // NonEmptyString 的类型为 string

8.3 使用 ReturnType 获取函数返回类型

typescript 复制代码
type GreetFunc = (name: string) => string;

type GreetReturn = ReturnType<GreetFunc>; // GreetReturn 的类型为 string

function greet(name: string): GreetReturn {
  return `Hello, ${name}!`;
}

8.4 使用 Parameters 获取函数参数类型

typescript 复制代码
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 使用 ThisParameterTypeOmitThisParameter 处理 this 参数

typescript 复制代码
type FuncWithThis = (this: Date, x: string) => void;

type ThisParam = ThisParameterType<FuncWithThis>; // ThisParam 的类型为 Date

type FuncWithoutThis = OmitThisParameter<FuncWithThis>; // FuncWithoutThis 的类型为 (x: string) => void

function example(this: ThisParam, x: string) {
  console.log(this.toUTCString(), x);
}

const date = new Date();
example.call(date, "hello");

8.6 使用 ThisType 在对象字面量中指定 this 类型

typescript 复制代码
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 进行自定义类型守卫

typescript 复制代码
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 断言创建只读对象

typescript 复制代码
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 排除 nullundefined

typescript 复制代码
type MaybeString = string | null | undefined;

type DefiniteString = NonNullable<MaybeString>; // DefiniteString 的类型为 string

function getString(): MaybeString {
  return "Hello";
}

const str: DefiniteString = getString()!;

8.10 使用 InstanceType 获取构造函数实例类型

typescript 复制代码
class Person {
  constructor(public name: string) {}
}

type PersonConstructor = typeof Person;

type PersonInstance = InstanceType<PersonConstructor>; // PersonInstance 的类型为 Person

const person: PersonInstance = new Person("Alice");

总结

TypeScript 提供了丰富的类型操作工具,使得开发者能够在编译阶段进行更严格的类型检查和更灵活的类型操作。通过理解和应用这些高级类型特性,可以显著提升代码的可维护性和可靠性。

相关推荐
不吃鱼的羊22 分钟前
ISOLAR软件生成报错处理(七)
java·前端·javascript
TE-茶叶蛋1 小时前
React-props
前端·javascript·react.js
安分小尧1 小时前
[特殊字符] 超强 Web React版 PDF 阅读器!支持分页、缩放、旋转、全屏、懒加载、缩略图!
前端·javascript·react.js
EndingCoder1 小时前
React从基础入门到高级实战:React 高级主题 - React Concurrent 特性:深入探索与实践指南
前端·javascript·react.js·前端框架
EndingCoder1 小时前
React从基础入门到高级实战:React 生态与工具 - React Query:异步状态管理
前端·javascript·react.js·前端框架
TE-茶叶蛋1 小时前
ReactJS 中的 JSX工作原理
前端·react.js·前端框架
水煮白菜王1 小时前
React 编译器
前端·react.js·前端框架
霸王蟹1 小时前
React 项目中封装 Excel 导入导出组件:技术分享与实践
前端·笔记·学习·react.js·typescript·excel·vite
wl_1 小时前
react-color-palette源码解析
前端·react.js·调色板
集成显卡1 小时前
图片压缩工具 | Electron+Vue3+Rsbuild开发桌面应用
前端·javascript·electron·vue