类型运算符(typeof)

TypeScript 中,typeof 操作符不仅可以用于获取值的运行时类型,还可以在类型上下文中使用,获取变量或属性的编译时类型。

基础示例

ts
const person = { name: "兜兜", age: 18 };

// 使用 typeof 获取 person 对象的类型
type Person = typeof person;

/* 
Person 类型等价于:
{
  name: string;
  age: number;
}
*/

function updatePerson(p: Person) {
  // p 的类型是 { name: string; age: number; }
}

复杂对象类型获取

ts
const userInfo = {
  id: 1,
  name: "兜兜",
  roles: ["admin", "user"] as const,
  permissions: { read: true, write: false, delete: false },
  createdAt: new Date(),
};

// 使用 typeof 获取复杂对象的完整类型
type UserInfoType = typeof userInfo;

/* UserInfoType 等价于: 
{  
  id: number; 
  name: string; 
  roles: readonly ["admin", "user"]; 
  permissions: { 
    read: boolean; 
    write: boolean; 
    delete: boolean; 
  }; 
  createdAt: Date; 
 }
 */

函数类型获取

  • Parameters 获取函数参数类型
  • ReturnType 获取函数返回值类型
ts
function calculateTotal(items: { price: number; quantity: number }[]) {
  return items.reduce((total, item) => total + item.price * item.quantity, 0);
}
// 获取函数的类型
type CalculateTotalType = typeof calculateTotal;

// 获取函数参数类型
type CalculateTotalParams = Parameters<typeof calculateTotal>;

// 获取函数返回值类型
type CalculateTotalReturnType = ReturnType<typeof calculateTotal>;

// 使用示例
const items = [
  { price: 10, quantity: 2 },
  { price: 5, quantity: 3 },
];
const total: CalculateTotalReturnType = calculateTotal(items); // number

类和构造函数类型

ts
class UserAccount {
  name: string;
  id: number;
  constructor(name: string, id: number) {
    this.name = name;
    this.id = id;
  }
  getInfo() {
    return `${this.name} (${this.id});`;
  }
}
const userInstance = new UserAccount("兜兜", 1);

// 获取实例类型
type UserInstanceType = typeof userInstance;

// 获取类的构造函数类型
type UserClassType = typeof UserAccount;

// 使用类构造函数类型创建工厂函数
function createUser(ctor: UserClassType, name: string, id: number): InstanceType<UserClassType> {
  return new ctor(name, id);
}
const newUser = createUser(UserAccount, "Murphy", 2);

枚举类型获取

ts
enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}

// 获取枚举类型
type DirectionType = typeof Direction;

// 在运行时使用枚举
function move(direction: Direction) {
  switch (direction) {
    case Direction.Up:
      console.log("Moving up");
      break;
    case Direction.Down:
      console.log("Moving down");
      break;
  }
}
// 使用 typeof 获取枚举成员的类型
type DirectionValue = DirectionType[keyof DirectionType];

数组和元组类型获取

ts
const COLORS = ["red", "green", "blue"] as const;
// 获取数组类型
type ColorsType = typeof COLORS;
// COLORS 的类型是 readonly ["red", "green", "blue"]
// 而不是 string[]

// 使用 typeof 创建基于常量数组的类型
type Color = (typeof COLORS)[number]; // "red" | "green" | "blue"
const validColors: Color[] = ["red", "green"]; // 只能是 COLORS 中的值

结构类型(interface)

ts
// noinspection JSAnnotator

interface User {
  name: string;
  id: number;
}

const user: User = {
  name: "兜兜",
  id: 2,
};

类使用接口声明

ts
interface User {
  name: string;
  id: number;
}

class UserAccount {
  name: string;
  id: number;

  constructor(name: string, id: number) {
    this.name = name;
    this.id = id;
  }
}

const user: User = new UserAccount("Murphy", 1);

函数定义

ts
interface User {
  name: string;
  id: number;
}

function getAdminUser(): User {
  //...
}

function deleteUser(user: User) {
  // ...
}

继承(extends)

ts
interface User {
  name: string;
  id: number;
}

interface AdminUser extends User {
  isAdmin: boolean;
}

const user: AdminUser = {};

组合类型 (|)

ts
type MyBool = true | false;

function getLength(obj: string | string[]) {
  return obj.length;
}

泛型(<>)

泛型为类型提供变量。一个常见的例子是数组。没有泛型的数组可以包含任何东西。具有泛型的数组可以描述数组包含的值。

ts
type StringArray = Array<string>;
type NumberArray = Array<number>;
type ObjectWithNameArray = Array<{ name: string }>;

可忽略参数 (?)

在函数中,我们可以使用?来表示一个参数是可选的。

ts
function f(x: number, y?: number) {
  return x + (y || 0);
}

非空断言 (!)

  • ! 是 TypeScript 的一个类型层面的操作符,用于告诉编译器:“我非常确定这个变量在此时不是 null 也不是 undefined”,因此可以安全使用其属性或方法,不会报类型错误。
  • 它只在编译阶段有效,编译后会被移除,对最终 JS 输出不起任何作用。
ts
a!.data;
a!.data.length;

交叉类型 (&)

交叉类型(Intersection Types)通过 & 运算符将多个类型合并为一个,表示同时具备所有类型的所有属性和方法。

typescript
interface Dog {
  dogName: string;
  dogAge: number;
}

interface Cat {
  catName: string;
  catAge: number;
}

type Pet = Dog & Cat;

const pet: Pet = {
  dogName: "Murphy",
  dogAge: 2,
  catName: "Murphy",
  catAge: 2,
};

缺省运算 (Omit)

使用 Omit 运算符可以删除对象中的某些属性。

typescript
type Pet = {
  dogName: string;
  dogAge: number;
  catName: string;
  catAge: number;
};

// 删除 catName 和 catAge
type Dog = Omit<Pet, "catName" | "catAge">;

挑选运算 (Pick)

使用 Pick 运算符可以挑选对象中的某些属性。

typescript
type Pet = {
  dogName: string;
  dogAge: number;
  catName: string;
  catAge: number;
};

type Dog = Pick<Pet, "dogName" | "dogAge">;

可选运算 (Partial)

使用 Partial 运算符可以将所有属性变为可选。

typescript
type Pet = {
  dogName: string;
  dogAge: number;
  catName: string;
  catAge: number;
};

type Dog = Partial<Pet>;

Optional的实现及原理

原理:

  • K extends keyof T:表示 K 必须是 "name" | "age" | "email" | "isAdmin" 中的一个或多个
  • Pick<T, K>:从 T 中挑选出指定的 K 属性,返回一个新类型
  • Partial<...>:将 Pick 出来的属性变成可选的
  • Omit<T, K>:从原始类型 T 中排除掉 K 属性
  • 最后将 OmitPartial 的结果通过交叉类型 & 组合起来
typescript
interface User {
  name: string;
  age: number;
  email: string;
  isAdmin: boolean;
}

interface UserOptional {
  name?: string;
  age?: number;
}

type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

type UserOptional = Optional<User, "name" | "age">;

记录类型(Record)

Record 是 TypeScript 中的一个实用类型,用于创建具有特定键值对类型的对象。它接受两个类型参数:键的类型和值的类型。

ts
// 示例:用户 ID 映射
type User = {
  name: string;
  id: number;
};

const users: Record<string, User> = {
  u1: { name: "兜兜", id: 2 },
  u2: { name: "Murphy", id: 1 },
};

你也可以使用 Record 创建简单的键值对集合:

ts
// 字符串到数字的映射
const scores: Record<string, number> = {
  math: 90,
  english: 85,
  science: 92,
};

使用联合类型作为键

你可以通过联合类型来限制键的取值范围:

ts
type Color = "red" | "green" | "blue";

const colorHex: Record<Color, string> = {
  red: "#FF0000",
  green: "#00FF00",
  blue: "#0000FF",
};
评论 隐私政策