TypeScript 中的接口(interface)与类型别名(type)
在 TypeScript 中,interface 和 type 是两个非常常用的工具,主要用来定义和约束数据的结构,它们在很多情况下可以互相替代。虽然它们有一些相似之处,但在功能和使用场景上还是有一些区别。
接口(interface)
什么是接口
interface
是 TypeScript 提供的一种用来定义对象结构的工具。它的主要目的是描述一个对象的形状(shape),包括它有哪些属性、属性的类型是什么,以及是否有可选属性等。
接口就像是给对象或者函数设定的一个“模板”,规定了它们应该长啥样、有哪些属性或者方法。
举个栗子,想象你去餐厅吃饭,菜单上写着各种菜名,每道菜都有自己的配料和做法,这就像是接口。比如“红烧肉”这道菜,接口就规定了它得有五花肉、酱油、糖这些配料,还得有炖煮这道工序。厨师做菜的时候,就得按照这个“模板”来,不然做出来的就不是红烧肉了。
在 TypeScript 里,如果你定义了一个接口,比如叫 Person,里面规定了要有 name(名字)和 age(年龄)这两个属性。那当你创建一个 Person 类型的对象时,就必须得有这两个属性,要不然编译器就会提醒你“不对劲,少了东西”。这样一来,代码就更有规范性了,大家一看就知道这个对象该有啥,用起来也更方便,不容易出错。
interface Person {
name: string; // 必须的字符串属性
age: number; // 必须的数字属性
}
const person: Person = {
name: "Simin",
age: 30,
};
可选属性
用 ?
表示可选属性。
interface Person {
name: string;
age?: number; // 可选属性
}
const person1: Person = { name: "Simin" }; // age 可以省略
const person2: Person = { name: "Simin", age: 25 }; // age 也可以存在
- 如果属性是可选的,你不能直接访问它的子属性,否则会报错,需要进行检查。
function printAge(person: Person) {
if (person.age) {
console.log(person.age.toFixed(2)); // 确保 age 存在 如果没有if 那么将会报错
}
}
只读属性
用 readonly
修饰的属性,表示该属性只能在对象创建时被赋值,之后不能修改。
interface Person {
readonly id: number; // 只读属性
name: string;
}
const person: Person = { id: 1, name: "Simin" };
person.name = "xxm"; // 可以修改 name
// person.id = 2; // 报错:只读属性不能被修改
接口的继承
接口支持继承,可以将多个接口组合在一起。
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
const myDog: Dog = {
name: "旺财",
breed: "Labrador",
};
接口合并
接口支持同名合并,会自动把多个同名接口的属性合并在一起。
interface Person {
name: string;
}
interface Person {
age: number;
}
const person: Person = { name: "Simin", age: 24 };
类型别名(Type)
什么是类型别名
type
用来为任意类型赋一个名字,可以是基本类型、对象类型、联合类型、交叉类型,就像是搭积木的图纸
你有一地的乐高积木块,你想搭个房子。那类就像是这个房子的设计图纸,图纸上画好了房子有几层、每层有几个房间、房间的门朝哪开、窗户在哪等等。在 TypeScript 里,类就是用来设计对象的图纸。
基本用法
type Person = {
name: string;
age: number;
};
const person: Person = { name: "Simin", age: 24 };
联合类型
type
可以用来定义联合类型(多个类型中的一种)。
type ID = string | number;
const id1: ID = "abc123";
const id2: ID = 12345;
交叉类型
type
也可以用来定义交叉类型(多个类型的组合)。
type HasName = { name: string };
type HasAge = { age: number };
type Person = HasName & HasAge;
const person: Person = { name: "Simin", age: 24 };
类型别名与函数
与接口类似,type
也可以用来定义函数类型。
type Add = (a: number, b: number) => number;
const add: Add = (x, y) => x + y;
类型别名与元组
type
可以定义元组类型(固定长度的数组)。
type Point = [number, number];
const point: Point = [10, 20];
类型别名的扩展
类型别名不支持直接继承,但可以通过交叉类型来扩展。
type Animal = { name: string };
type Dog = Animal & { breed: string };
const dog: Dog = { name: "Buddy", breed: "Labrador" };
接口 vs 类型别名
接口就像是个“标准模板”。就好比你去参加一个晚会,主办方给你发了个着装要求的清单,上面写着得穿正装、打领带、穿皮鞋啥的,这就相当于接口。它只告诉你得准备些啥,但具体怎么弄,用啥牌子的领带、啥款式的皮鞋,它不管。在 TypeScript 里,接口就是用来定义对象或者函数的结构,规定了得有哪些属性、方法,但不关心这些属性和方法具体怎么实现。比如一个 Car 接口,规定了车得有 wheels(轮子)和 start(启动)方法,但不告诉你轮子是啥材质的、启动是怎么个启动法
类就不一样了,类是个实实在在的“蓝图”。还拿做衣服打比方,裁缝师傅有个衣服的纸样,上面不仅有衣服的款式、尺寸,还详细标注了怎么剪布、怎么缝,这就跟类似的。类不仅定义了对象的结构,还实现了具体的功能。比如你有个 Car 类,它继承了 Car 接口,那它就得有 wheels 属性,还得实现 start 方法。而且你还可以在类里添加更多的属性和方法,比如 color(颜色)属性、stop(停止)方法啥的,把车的各种细节都给完善了
总结一下,接口就是个“标准”,告诉你得有啥;类是个“实现”,不仅有接口规定的那些,还把具体的功能都给实现了。接口更侧重于规范,类侧重于具体实现
如果主要是定义 对象的结构,优先选择 interface
。
如果需要描述 联合类型、交叉类型、元组,或者为复杂类型取别名,则使用 type
。
相似点
- 都可以用来定义对象结构。
- 都可以用来定义函数类型。
- 都能与类一起使用
不同点
特性 | interface | type |
---|---|---|
扩展性 | 支持继承和合并 | 支持交叉类型,但不支持合并 |
表达能力 | 专注于描述对象 | 可以表示任意类型 |
关键字 | 使用 interface 定义 | 使用 type 定义 |
内置支持 | TypeScript | 对接口有更多优化 |