常识来了
白蓝主题五 · 清爽阅读
首页  > 软件进阶

TypeScript定义类型:让代码更靠谱的写法

写JavaScript多了,总会遇到那种让人头大的情况:明明传了个对象,结果运行时突然报错,说某个方法不存在。一查才发现,传进去的根本不是预期的结构。这种问题在项目大了之后尤其常见。TypeScript这时候就派上用场了,尤其是它的“定义型”功能,能提前把数据长什么样给定下来。

用 interface 描述对象结构

比如你做一个用户管理系统,经常要处理用户信息。你可以用 interface 把用户的结构固定住:

interface User {
  id: number;
  name: string;
  email?: string;
  isActive: boolean;
}

这样,每次函数接收一个 user 参数时,就可以明确指定它必须符合 User 的结构:

function printUserInfo(user: User) {
  console.log(`${user.name} - ${user.email || '无邮箱'}`);
}

如果有人传了个没 name 的对象进来,编辑器立马标红提醒,不用等到运行出错才发觉。

type 也能定义类型,和 interface 有啥不一样?

type 更灵活一点,不只能定义对象,还能给原始类型起别名,或者组合出复杂类型:

type ID = string | number;

type Status = 'active' | 'inactive' | 'pending';

type Point = {
  x: number;
  y: number;
};

上面这段里,ID 可以是字符串或数字,适合那种用户ID可能是自增数也可能是UUID的场景。Status 直接限制取值范围,避免拼错状态名。

联合类型和可选属性很实用

实际开发中,数据往往不是非黑即白。比如接口返回的结果,可能是成功数据,也可能是错误对象。可以用联合类型来覆盖这种情况:

interface Success {
  code: 0;
  data: User;
}

interface Error {
  code: number;
  message: string;
}

type ApiResponse = Success | Error;

这样调用 API 的函数返回值设为 ApiResponse,处理时就得判断 code 是不是 0,逻辑更清晰,也不容易漏掉错误处理。

类型别名配合泛型,复用性更强

如果你写的是组件库或工具函数,泛型加类型别名会很顺手。比如封装一个通用的响应结构:

type ApiResponse<T> = {
  success: true;
  data: T;
} | {
  success: false;
  error: string;
};

const response: ApiResponse<User> = {
  success: true,
  data: { id: 1, name: 'Alice', isActive: true }
};

这里的 ApiResponse<User> 明确告诉别人,成功时拿到的是 User 类型的数据。换别的地方用 ApiResponse<Product> 也一样成立,不用重复写结构。

小技巧:用 typeof 和 keyof 提高安全性

有时候你想让某个变量的键名必须来自某个对象,可以用 keyof。比如有个配置对象:

const config = {
  apiHost: 'https://api.example.com',
  timeout: 5000,
  debug: true
};

type ConfigKey = keyof typeof config;

function getSetting(key: ConfigKey): any {
  return config[key];
}

getSetting('apiHost'); // ✅
getSetting('notExist'); // ❌ 编译报错

这样拼错 key 名的时候,TypeScript 就能立刻发现,省去调试时间。

类型定义不是为了多写几行代码,而是为了让协作更顺畅、维护更容易。一开始可能觉得麻烦,但改几个字母就能看到错误提示的感觉,久了就离不开了。