- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
本文基于开源项目The Concise TypeScript Book(法文原版章节与英文原版章节)编写。TypeScript 中
interface(接口)与type(类型别名)是描述数据形状的两大核心手段。本文将从二者最常用的声明语法讲起,系统梳理基础类型、对象类型、Union(联合)与 Intersection(交叉)类型,并对照仓库中《Differences between Type and Interface》等章节,深入解析二者的声明合并、扩展方式与适用场景。读完本文,你将能根据实际需求准确选择interface或type,写出类型安全、可维护的 TypeScript 代码。
一、Interface 与 Type 的通用声明语法
在 TypeScript 中,interface(接口)用于定义对象的形状:它精确声明一个对象必须拥有的属性名、属性类型以及方法签名。定义一个接口的通用语法如下:
interface InterfaceName { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... }与之对应,type 类型别名也可以声明一个等价的"对象形状",语法非常相似:
type TypeName = { property1: Type1; // ... method1(arg1: ArgType1, arg2: ArgType2): ReturnType; // ... };逐项解读这段语法:
interface InterfaceName或type TypeName:定义接口或类型别名的名称;property1: Type1:声明接口/类型对象的属性及其对应类型。可以声明多个属性,每条属性声明之间用分号;分隔;method1(arg1: ArgType1, arg2: ArgType2): ReturnType:声明方法。方法由方法名、括号内的参数列表以及返回类型组成。同样可以声明多个方法,彼此用分号分隔。
一个完整的接口示例:
interface Person { name: string; age: number; greet(): void; }一个完整的类型别名示例:
type TypeName = { property1: string; method1(arg1: string, arg2: string): string; };说明:原文档(如 interface-and-type.md)中的示例带有
<!-- skip -->注释标记,表示该代码片段是语法示意(如Type1、ArgType1为占位符),并不能直接编译通过;实际项目中请使用真实类型名替换。
在 TypeScript 中,类型(types)用来定义数据的形状并强制类型检查。根据具体使用场景的不同,TypeScript 有多种常用的类型定义语法,下面逐一展开。
二、基础类型(Basic Types)
对变量声明进行类型标注是最基础的类型定义方式。仓库中《Type Annotations》章节明确指出:对于使用var、let和const声明的变量,可以可选地添加类型标注。以下是最常见的基础类型写法:
let myNumber: number = 123; // number 类型 let myBoolean: boolean = true; // boolean 类型 let myArray: string[] = ['a', 'b']; // 字符串数组 let myTuple: [string, number] = ['a', 123]; // 元组(tuple)要点说明:
number、boolean、string属于 TypeScript 的基础类型(Primitive Types),此外还有bigint、symbol、null、undefined、any等;string[]表示元素类型为string的数组,也可写作Array<string>;- 元组
[string, number]是固定长度、按顺序规定各位置元素类型的数组形态,仓库另有《Tuple Type (Anonymous)》与《Fixed Length Tuple》专门讲解; - 需要留意的是,TypeScript 对简单类型的推断能力很强,
const x: number = 1这类显式标注在多数场景下并非必需。仓库建议的一般原则是:给函数签名(参数与返回类型)做标注,但通常不必为函数体内部的局部变量做标注;对象字面量则始终显式标注类型。
三、对象与接口(Objects and Interfaces)
对象类型描述一个对象的形状:指明对象属性的名称、类型,以及这些属性是必需还是可选(详见仓库《Object Types》章节)。对象的类型既可以用interface声明,也可以用type别名声明,还可以匿名内联地直接写在变量或函数参数上:
const x: { name: string; age: number } = { name: 'Simon', age: 7 };上述代码直接在变量声明处内联了一个匿名对象类型,要求x必须同时具备name: string与age: number两个属性。
对象类型的两种正式定义方式:
// 方式一:interface interface User { name: string; age: number; email?: string; // 问号表示可选属性 } // 方式二:type 别名 type Point = { x: number; y: number; };关于可选属性:在属性名末尾追加问号?即可声明该属性可选。仓库《Optional Properties》章节进一步给出了"可选属性配合默认值"的典型用法:
type X = { a: number; b?: number; // 可选 }; const x = ({ a, b = 100 }: X) => a + b;这里b未传入时使用默认值100,实现"可选但有兜底值"的效果。
匿名对象类型同样可以出现在函数参数中(仓库《Object Types》章节的示例):
const sum = (x: { a: number; b: number }) => x.a + x.b; console.log(sum({ a: 5, b: 1 })); // 6四、Union 与 Intersection 类型
4.1 Union 联合类型:|
Union 类型表示一个值可以是若干种类型之一,使用|符号连接每种可能的类型(详见仓库《Union Type》章节):
type MyType = string | number; // Union 类型 let myUnion: MyType = 'hello'; // 可以是 string myUnion = 123; // 也可以是 number基本示例:
let x: string | number; x = 'hello'; // 合法 x = 123; // 合法4.2 Intersection 交叉类型:&
Intersection 类型表示一个值同时拥有两个或多个类型的全部属性,使用&符号连接(详见仓库《Intersection Types》章节):
type TypeA = { name: string }; type TypeB = { age: number }; type CombinedType = TypeA & TypeB; // Intersection 类型 let myCombined: CombinedType = { name: 'John', age: 25 }; // 同时具备 name 与 age 的对象仓库中的另一组示例更加直观:
type X = { a: string; }; type Y = { b: string; }; type J = X & Y; // Intersection const j: J = { a: 'a', b: 'b', };需要强调:Union 类型在运行时由你赋值给变量的值决定;而 Intersection 类型在结构上要求对象必须同时满足所有组成类型的约束。
五、Type 与 Interface 的关键差异
这是本主题最容易被混淆的部分。仓库专门设有《Differences between Type and Interface》章节,以下差异在该章节均有明确示例与论述。
5.1 声明合并(Declaration Merging):Interface 独有
Interface 支持声明合并(declaration merging):你可以多次声明同名 interface,TypeScript 会将它们自动合并为一个包含全部属性与方法的单一接口。Type 不支持声明合并。这一特性在你想"在不修改原始定义的前提下,为已有类型补充功能、修补缺失或错误类型"时非常有用。
interface A { x: string; } interface A { y: string; } const j: A = { x: 'xx', y: 'yy', };如果对A使用两次type声明同名别名,TypeScript 会直接报"重复标识符"错误。
5.2 扩展其他类型/接口:语法不同
Interface 和 Type都可以扩展其他类型/接口,但语法不同:
- Interface 使用
extends关键字继承其他接口的属性和方法,且可以同时继承多个接口。但 interface不能扩展复杂类型(例如 union 类型)。
interface A { x: string; y: number; } interface B extends A { z: string; } const car: B = { x: 'x', y: 123, z: 'z', };- Type 使用
&运算符(交叉类型)把多个类型组合成一个类型:
interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', };仓库《Extending Types》章节对扩展能力做了更完整的总结:
- interface 可以扩展 interface,支持多继承:
interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; }- type 之间用交叉类型扩展:
type A = { a: number; }; type B = { b: number; }; type C = A & B;- 可以用 interface 扩展 type,但反过来不行:
type A = { a: string; }; interface B extends A { b: string; } // 合法:interface extends type // 反例:type 无法 extends interface // type C = B extends ... // 语法不允许,只能改用 &5.3 Union/Intersection 的表达能力:Type 更灵活
在定义 Union 与 Intersection 类型时,Type 更加灵活:借助|与&运算符可以轻松创建联合与交叉类型。而 interface 虽然可以间接地表示 union(例如type C = A | B这种"两个 interface 的联合"),但没有内建对 intersection 的支持。
用 type 定义业务模型中常见的"组合"形态:
type Department = 'dep-x' | 'dep-y'; // Union type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // IntersectionInterface 与 union 结合使用的示例:
interface A { x: 'x'; } interface B { y: 'y'; } type C = A | B; // 两个 interface 的联合5.4 小结:何时用 Interface,何时用 Type
综合原文档与仓库相关章节,可以提炼出如下选用参考:
| 场景 | 推荐 | 原因 |
|---|---|---|
| 需要声明合并(同名多次声明、类型增强) | interface | 只有 interface 支持 declaration merging |
需要表达 union(A \| B) | type | interface 无内建 union 语法 |
需要表达 intersection(A & B) | type | interface 无内建交叉能力 |
| 扩展对象形状、面向对象继承 | interface extends | 语义清晰,支持多继承 |
| 基于已有类型组合出新类型、映射类型、条件类型等复杂形态 | type | 表达式能力强,可复用 union/交叉/泛型工具类型 |
六、进阶延伸:结合泛型与索引签名
虽然本主题聚焦interface与type的声明与差异,但二者都能与 TypeScript 的其他类型能力组合使用,这里补充两点与"类型定义"紧密相关的实战延伸。
6.1 泛型让类型定义可复用
泛型允许把类型参数化,从而让接口与类型别名在不同类型上复用(详见仓库《Generics》章节)。例如泛型函数与泛型类:
function identity<T>(arg: T): T { return arg; } const a = identity('x'); const b = identity(123); class Container<T> { private item: T; constructor(item: T) { this.item = item; } getItem(): T { return this.item; } } const numberContainer = new Container<number>(123); console.log(numberContainer.getItem()); // 123泛型参数还可以用extends约束,要求传入类型必须满足某个形状:
const printLen = <T extends { length: number }>(value: T): void => { console.log(value.length); }; printLen('Hello'); // 5 printLen([1, 2, 3]); // 3 printLen(123); // 编译错误:number 没有 length 属性6.2 索引签名定义"键未知"的对象
当对象的键在编写代码时未知时,可以用索引签名声明类型(详见仓库《Index Signatures》与《Type Indexing》):
type Dictionary<T> = { [key: string]: T; }; const myDict: Dictionary<string> = { a: 'a', b: 'b' }; console.log(myDict['a']); // 'a'索引键支持string、number与symbol。需要特别注意的是:JavaScript 会自动把number索引转换为string索引,因此k[1]与k["1"]取到的是同一个值:
type K = { [name: string | number]: string; }; const k: K = { x: 'x', 1: 'b' }; console.log(k['x']); console.log(k[1]); // 'b' console.log(k['1']); // 与 k[1] 结果相同七、结语与实战建议
综合原文档与仓库中《Type Annotations》、《Differences between Type and Interface》、《Extending Types》、《Object Types》等章节,可以把本文的核心结论浓缩为四条实战建议:
- 描述对象形状:
interface与type均可胜任,二者在普通对象上能力几乎等价,按团队约定选用其一保持一致即可; - 需要合并、增强、补丁:优先用
interface,因为它支持声明合并; - 需要 union / intersection / 映射等复杂类型运算:优先用
type,表达能力更强; - 写清楚签名、让编译器做推断:函数参数与返回值建议显式标注类型(
const sum = (a: number, b: number): number => a + b),函数体内的局部变量交给 TypeScript 推断,对象字面量则始终标注类型。
关于更多类型定义细节(可选属性、只读属性、索引签名、字面量类型、映射类型、条件类型等),可继续阅读仓库中的《Object Types》、《Optional Properties》、《Readonly Properties》、《Type Manipulation》等章节;其他语言版本的对应章节位于 fr-fr 等翻译目录下,内容一致,可作为多语言参考。
- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
相关推荐
The Concise TypeScript Book 精讲:Interface 与 Type 的语法、差异与实战取舍
The Concise TypeScript Book 精讲:Interface 与 Type 的语法、差异与实战取舍 本文以开源项目 The Concise
文档教程The Concise TypeScript Book 精读:Type 与 Interface 的差异全解析
The Concise TypeScript Book 精读:Type 与 Interface 的差异全解析 本篇技术指南围绕开源书籍 The Concise
文档教程The Concise TypeScript Book 精读:type 与 interface 的核心差异与实战选型
The Concise TypeScript Book 精读:type 与 interface 的核心差异与实战选型 导读 type (类型别名)与 inter
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考