TypeScript 类型别名(type)

在上一节中,我们学习了如何使用 “接口(interface)” 来约束对象的形状。TypeScript 的接口非常强大,但它有一个局限性:只能用来定义 “对象”。

在实际开发中,有时我们不仅仅需要给对象约束形状,还需要给普通的基础类型(比如字符串、数字)、甚至复杂的组合类型起一个具有语义化的名字。此时,就需要用到 TypeScript 的 “类型别名(type)”了。

TypeScript 类型别名是什么?

TypeScript 类型别名,顾名思义就是:给一个 “已经存在的类型” 起一个 “小名”(或者叫别名)。

语法:

type 别名 = 具体类型;

说明:

类型别名的主要作用是为了让代码更加简洁、可读性更强。就像在日常生活中,一个人可能有大名,也有小名,但它们指代的都是同一个人。

示例 1:给基础类型起别名

// 定义别名
type Name = string;
type Age = number;

// 使用别名定义变量
const userName: Name = "Jack";
const userAge: Age = 20;

console.log(userName);
console.log(userAge);

运行结果如下。

Jack
20

分析:

在这个例子中,我们给 string 类型起了个别名叫 “Name”,并且给 number 类型起了个别名叫 “Age”。也就是说,Name 和 string 指的是同一个类型,而 Age 和 number 指的也是同一个类型。

虽然看起来像是脱裤子放屁,但在某些十分复杂的泛型推导场景中,给基础类型起别名能够让代码语义更清晰。

示例 2:给数组类型起别名

type numArr = number[];
type strArr = string[];

const prices: numArr = [10, 20, 30];
const colors: strArr= ["red", "green", "blue"];

console.log(prices[0]);
console.log(colors[1]);

运行结果如下。

10
green

分析:

除了基础类型之外,我们还可以使用 type 给数组、对象、接口等起一个别名。

TypeScript 使用 “类型别名” 定义对象

在 TypeScript 中,虽然接口(interface)可以用来定义对象,但类型别名(type)同样也能做到,而且两者语法极其相似。

示例 3:type 定义对象

type User = {
    name: string;
    age: number;
    hobby?: string;    // 同样支持可选属性
};

const currentUser: User = {
    name: "Jack",
    age: 20
};

console.log(currentUser.name);

运行结果如下。

Jack

分析:

type 定义对象,和interface 定义对象,两者使用起来可以说是一模一样。

TypeScript 类型别名的用途

这里小伙伴们肯定会问:“既然已经可以使用 interface 来表示对象,那 TypeScript 为什么还要多搞一个 type 出来呢?”

这是因为 interface 只能用于表示对象,而 type 可以用于表示任何类型。

1. 定义元组类型

我们前面已经学习过元组了,元组的类型定义通常是一长串的方括号。如果到处写,代码可读性性会非常差。此时我们可以用 type 给它封装一下。

示例 4:type 定义元组类型

// 为元组起个别名
type Point = [number, number];

// 复用别名
const startPoint: Point = [10, 20];
const endPoint: Point = [100, 200];

console.log(startPoint[0]);

运行结果如下。

10

分析:

我们为 “[number, number]” 起了一个别名 “Point”,后面就不用每次都写 “[number, number]”,而是直接使用 “Point” 就可以了。这样的代码更加优雅,复用性也更强。

2. 定义联合类型(最常用)

在真实的业务开发中,某个变量的值可能只能是固定的几个字符串之一。比如订单状态,只能是 "pending"、"success" 或 "failed"。这个时候,Type 就派上大用场了。

示例 5:type 定义联合类型

// 定义一个联合类型
type OrderStatus = "pending" | "success" | "failed";

// 正常赋值
const currentStatus: OrderStatus = "success";
console.log(currentStatus);

// 错误赋值:如果拼写错误或者赋了不在约定里的值,直接报错
// const wrongStatus: OrderStatus = "loading";
// 报错:不能将类型“"loading"”分配给类型“OrderStatus”。

运行结果如下。

success

分析:

像这个例子的用法,在企业级项目中非常普遍。通过 type 配合联合类型(多个类型用 “|” 隔开),我们直接在类型层面锁死了变量的取值范围,甚至连 if...else 校验都省了。只要你赋的值不对,TypeScript 就会立马报错拦截。

实际上,无论是基础类型、对象、联合类型还是元组,我们都可以用 type 来封装并起个直观的名字。它极大程度地提升了代码的复用性和可读性。

3. 定义函数类型

在组件化开发中,我们经常需要把一个函数当作参数传来传去(比如组件的各种回调事件)。如果每次都把又长又臭的函数参数和返回值写在组件的接收处,代码会变得非常臃肿。

此时,我们可以使用 type 来给函数结构起个别名,这也是最优雅的做法。

示例 6:type 定义函数类型

// 定义一个函数类型:接收两个数字,返回一个数字
type CalculateFn = (a: number, b: number) => number;

// 使用别名来约束具体的函数
const add: CalculateFn = (x, y) => {
    return x + y;
};

const multiply: CalculateFn = (x, y) => {
    return x * y;
};

console.log(add(10, 20));
console.log(multiply(10, 20));

运行结果如下。

30
200

分析:

在这个例子中,我们使用 type 提取了一个通用的函数结构 CalculateFn。后面无论是写加法还是乘法,只要直接套用这个别名,编辑器就会自动对 x 和 y 进行数字类型的推导。

那么 type 和 interface 到底怎么选?

既然 type 和 interface 都可以用来定义对象,那我们在项目开发中,到底应该用哪一个呢?

它们俩虽然非常相似,但有一个非常核心的底层区别:interface 支持同名合并,而 type 不支持。

示例 7:接口的同名合并

// 第 1 次定义 User 接口
interface User {
    name: string;
}

// 第 2 次定义同名的 User 接口
interface User {
    age: number;
}

// TypeScript 会自动把它们合并成一个完整的 User
const currentUser: User = {
    name: "Jack",
    age: 20
};

console.log(currentUser);

运行结果如下。

{ name: 'Jack', age: 20 }

分析:

在这个例子中,如果我们使用 type 去重复定义同名的类型,此时TypeScript 会直接报错:

报错:标识符“User”重复

在实际开发中,对于 interface 和 type,我们应该遵循以下最佳实践:

  • 优先使用 interface 定义对象:如果你是在定义普通的业务数据结构(比如后端的 API 返回值、Vue/React 组件的 props 对象),并且希望它具备良好的扩展性,应该优先使用 interface。这也是 TypeScript 官方强烈推荐的做法。
  • 只在特定场景使用 type:当你需要给基础类型起别名,或者你需要用到联合类型(如 "success" | "failed")、元组类型时,不要犹豫,直接上 type。

一句话总结就是:对象和类的结构约束,交给 interface;灵活多变的类型组合与别名,交给 type。

上一篇: TypeScript 接口

下一篇: TypeScript 联合类型

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号