TypeScript类型学习

46 阅读4分钟

类型学习

值到类型转换

  1. as const
// 数值
const type = ["primary", "secondary"] as const;

type buttonType = (typeof type)[number];
// type buttonType = "primary" | "secondary"

type IButton = { [key in buttonType]: string };
/* type IButton = {
    primary: string;
    secondary: string;
} */
  1. 枚举
enum type {
  primary = "primary",
  secondary = "secondary",
}
type buttonType = type;
type IButton = { [key in buttonType]: string };

// type IButton = {
//     primary: string;
//     secondary: string;
// }

常见场景:

  • 创建组件样式映射(如本例)。
  • 实现枚举到值的映射。
  • 从配置数组自动生成类型定义。

优势

  • 类型安全:任何不在 type 数组中的键都会导致编译错误。
  • 可维护性:修改 type 数组时,类型系统会自动更新所有依赖。
  • 代码简洁:避免手动重复书写类型定义。

类型条件

通过 extends 关键字加三目运算符来实现类型条件判断。

// 类型定义
interface Human {
  creative: true;
  name?: string;
}

interface Animal {
  species: string;
}

type Creature = Human | Animal;
// 使用示例
const creature1 = { creative: true, name: "Alice" };
const creature2 = { species: "cat" };
// 类型判断
type species1 = typeof creature1 extends { creative: boolean } ? Human : Animal;
// type species1 = Human
type species2 = typeof creature2 extends { creative: boolean } ? Human : Animal;
// type species2 = Animal

泛型

泛型是指在定义函数、接口或类时,不指定具体的类型,而是在使用时再指定类型。

// 泛型定义
type creatureType<T> = T extends { creative: boolean } ? Human : Animal;
// 类型判断
type species1 = creatureType<typeof creature1>;
// type species1 = Human
type species2 = creatureType<typeof creature2>;
// type species2 = Animal

剔除方法的使用

type NonFunctionPropertyNames<T> = {
  [K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
// {}[keyof T] 是获取对象值的类型,never就不显示代表剔除方法,其他的就是值的类型。

type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
// 调用pick方法,第一个参数是对象,第二个参数是类型。
// 获取对象中不是方法的属性

class UserService {
  id: number = 1;
  name: string = "John";
  save(): void {}
  delete(): void {}
}

type remain = NonFunctionPropertyNames<UserService>;
// type remain = "id" | "name"

type UserData = NonFunctionProperties<UserService>;
// UserData is: { id: number; name: string }
// 方法被自动过滤掉了!

类的定义(UserService) 既是值(类的构造函数),也是类型(类的实例结构),因此泛型中的 T 可以直接传入类(UserService)

在类型工具的上下文中(如 type 定义、泛型约束),类名默认指其类型。 如果你想引用类的构造函数类型,需要显式使用 typeof UserService。

is 关键字实现类型守卫

缩小类型范围

//类型定义
type admin = {
  name: string;
  age: number;
  token: "admin";
};
type normal = {
  name: string;
  age: number;
  id: "normal";
};
// 使用示例
const user: any = {
  name: "123",
  age: 123,
  id: "normal",
};
// 类型守卫
function isAdmin(user: admin | normal): user is admin {
  return "token" in user;
}
//它的作用是告诉编译器:“当这个函数返回 true 时,传入的参数类型是我指定的类型”。
//使用

if (isAdmin(user)) {
  // 缩小了user类型范围
  user;
  // const user: admin
  console.log(user.token);
}

在 TypeScript 中,is 关键字是类型守卫的核心语法,它的作用是:

  • 在运行时检查值的类型。
  • 在编译时为 TypeScript 提供类型信息,从而实现类型安全。

通过 is,可以编写更精确、更安全的类型检查逻辑,让 TypeScript 编译器理解并应用类型缩小。

infer 推断

窥探类型内部结构

// 函数返回值推断
type myReturn<T> = T extends () => infer R ? R : never;
type exampleReturn = myReturn<() => Promise<string>>;
// type exampleReturn = Promise<string>

多个变量推断

type functioninfer<T> = T extens (first: infer A,second: infer B) => infer R ? {
    args: [A ,B],
    return: R
} : never

提取数组元素

type ArrayEle<T> = T extends (infer R)[] ? R : never;

Enum 枚举

在 TypeScript 中,enum(枚举)既可以作为值使用(在运行时有效),也可以作为类型使用(在编译时进行类型检查),这使得枚举在很多场景下都非常方便。

  1. 用作值
enum Status {
  Pending = 0,
  Success = 1,
  Error = 2,
}

// 作为值使用
const currentStatus = Status.Success; // 赋值(值为 1)
console.log(currentStatus); // 运行时输出:1

// 作为函数参数
function handleStatus(status: Status) {
  // ...
}
handleStatus(Status.Pending); // 传递枚举值(合法)
  1. 用作类型
  • 值到类型转换
  • 值作为属性名
enum obk {
  name = "名字",
  age = 123,
  token = "admin",
}

type exp = {
  [obk.name]: string;
};
// type exp = {
//     "名字": string;
// }
const lss: exp = {
  名字: "xxx",
};

类型工具

代码美化 / 简化

type Pretty<T, K extends keyof T> = {
    [P in K]: T[P]
}

/**
 * 美化输出,提高开发效率
 * type A = { a: string };
 * type B = { b: string };
 * type C = A & B;
 * // 编译器显示
 * type C = A & B;
 * // 美化后显示
 * type PrettyC = Pretty<A & B>;
 * // type PrettyC = {
 * //     a: string;
 * //     b: string;
 * // }
 */

必选属性

type WithRequired<T, K extends keyof T> = T & {
  [P in K]-?: T[P];
};

/**
 *
 * type exam = {
 *  ls?: number;
 *  arrive?: number;
 *  name: string;
 * }
 * type WithRequired<T, K extends keyof T> = T & {
 *   [P in K]-?: T[P];
 * };
 * type mid2 = WithRequired<exp, "ls" | "arrive">;
 * //  type mid2 = exp & {
 * //   ls: number;
 * //   arrive: number;
 * //  }
 * 可配合Pretty使用,美化输出
 */