☰
The Concise TypeScript Book 精讲:Interface 与 Type 的定义语法、Union/Intersection 与二者差异
2026/9/26 3:57:50 网站建设 项目流程
  • 文档
  • 教程

【免费下载链接】typescript-book

The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.

项目地址:https://gitcode.com/gh_mirrors/typ/typescript-book
点击查看免费下载

本文基于开源项目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; // Intersection

Interface 与 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)typeinterface 无内建 union 语法
需要表达 intersection(A & B)typeinterface 无内建交叉能力
扩展对象形状、面向对象继承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》等章节,可以把本文的核心结论浓缩为四条实战建议:

  1. 描述对象形状:interface与type均可胜任,二者在普通对象上能力几乎等价,按团队约定选用其一保持一致即可;
  2. 需要合并、增强、补丁:优先用interface,因为它支持声明合并;
  3. 需要 union / intersection / 映射等复杂类型运算:优先用type,表达能力更强;
  4. 写清楚签名、让编译器做推断:函数参数与返回值建议显式标注类型(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.

项目地址:https://gitcode.com/gh_mirrors/typ/typescript-book
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询