TypeScript 接口(interface)完全指南:语法、特性与实战技巧

TypeScript 接口(interface)完全指南:语法、特性与实战技巧

🔥 吃透 TypeScript ,interface 的使用方法、继承规则、接口合并,以及与 type 的核心区别

一、接口的基础用法

1. 接口的定义与实现

接口通过 interface 关键字定义,内部描述对象的属性和方法类型。任何使用该接口作为类型的对象,都必须严格遵循接口的结构约定。

typescript 复制代码
// 定义接口 Person
interface Person {
  firstName: string;
  lastName: string;
  age: number;
}

// 实现接口:对象必须包含 firstName、lastName、age 三个属性
const p: Person = {
  firstName: 'John',
  lastName: 'Smith',
  age: 25
};

// 错误示例:缺少 lastName 属性,不符合接口约定
// const p2: Person = { firstName: 'Jane', age: 22 };

2. 提取接口属性的类型

与对象类型一致,接口支持使用方括号 [] 提取某个属性的具体类型,便于单独复用该类型。

typescript 复制代码
interface Foo {
  a: string;
  b: number;
}

// 提取属性 a 的类型 → string
type AType = Foo['a'];
// 提取属性 b 的类型 → number
type BType = Foo['b'];

const aVal: AType = 'hello';
const bVal: BType = 123;

二、接口的成员类型

接口的成员包含 5 种形式:对象属性对象属性索引对象方法函数构造函数,覆盖了对象的所有常见语法。

1. 对象属性

这是接口最基础的成员类型,通过 属性名: 类型 的形式声明,支持可选属性和只读属性。

typescript 复制代码
interface Point {
  // 必选属性
  x: number;
  y: number;
  // 可选属性:属性名后加 ?
  z?: number;
  // 只读属性:属性名前加 readonly
  readonly id: string;
}

const point: Point = {
  x: 10,
  y: 20,
  id: 'P001'
  // z 可选,可省略
};

// 错误示例:只读属性 id 无法修改
// point.id = 'P002';

属性分隔规则 :属性之间可用 ;, 分隔,最后一个属性的结尾分隔符可省略。

2. 对象的属性索引

当对象的属性名不确定(如动态键名)时,可使用属性索引 描述属性名和属性值的类型,支持 stringnumbersymbol 三种索引类型。

(1)字符串索引

用于约束「属性名为字符串」的对象,所有字符串属性的类型必须符合索引声明。

typescript 复制代码
// 字符串索引:属性名为 string,属性值为 number
interface StringMap {
  [prop: string]: number;
}

const map: StringMap = {
  a: 1,
  b: 2
};

// 错误示例:属性 c 的值为 string,不符合索引类型约束
// const map2: StringMap = { a: 1, c: 'hello' };
(2)数值索引

用于约束「属性名为数值」的对象,常用来描述类数组对象或数组。

typescript 复制代码
// 数值索引:属性名为 number,属性值为 string
interface NumberMap {
  [prop: number]: string;
}

// 数组符合数值索引约束
const arr: NumberMap = ['a', 'b', 'c'];
// 类数组对象也符合约束
const obj: NumberMap = {
  0: 'x',
  1: 'y'
};
(3)索引的约束规则
  • 唯一性:一个接口中最多只能定义一个字符串索引、一个数值索引。
  • 兼容性 :若同时定义字符串索引和数值索引,数值索引的类型必须兼容字符串索引的类型(因为 JS 会将数值属性名自动转为字符串)。
typescript 复制代码
// 错误:数值索引类型(string)与字符串索引类型(number)不兼容
interface A {
  [prop: string]: number;
  [prop: number]: string;
}

// 正确:数值索引类型(number)兼容字符串索引类型(number)
interface B {
  [prop: string]: number;
  [prop: number]: number;
}

3. 对象的方法

接口中声明对象方法有三种写法,效果完全一致,推荐使用函数签名写法(更贴近方法的定义习惯)。

typescript 复制代码
// 写法 1:函数签名(推荐)
interface MethodInterface1 {
  f(x: boolean): string;
}

// 写法 2:箭头函数类型
interface MethodInterface2 {
  f: (x: boolean) => string;
}

// 写法 3:函数类型字面量
interface MethodInterface3 {
  f: { (x: boolean): string };
}

// 实现接口方法
const obj1: MethodInterface1 = {
  f(x) {
    return x ? 'true' : 'false';
  }
};
方法的重载

接口支持函数重载,即声明多个同名方法的不同参数和返回值类型,无需给出实现,只需约定类型即可。

typescript 复制代码
// 接口内声明方法重载
interface OverloadInterface {
  f(): number;
  f(x: boolean): boolean;
  f(x: string, y: string): string;
}

// 实现重载方法:需在外部定义函数实现所有重载类型
function func(): number;
function func(x: boolean): boolean;
function func(x: string, y: string): string;
function func(
  x?: boolean | string,
  y?: string
): number | boolean | string {
  if (x === undefined && y === undefined) return 1;
  if (typeof x === 'boolean') return x;
  if (typeof x === 'string' && typeof y === 'string') return x + y;
  throw new Error('参数错误');
}

// 部署重载方法
const obj: OverloadInterface = { f: func };

4. 函数

接口可以直接声明独立的函数类型,用于约束函数的参数和返回值。

typescript 复制代码
// 声明函数接口:接收两个 number 参数,返回 number
interface Add {
  (x: number, y: number): number;
}

// 实现函数接口
const myAdd: Add = (x, y) => x + y;
console.log(myAdd(1, 2)); // 输出 3

5. 构造函数

接口中使用 new 关键字可以声明构造函数类型,用于约束类的构造函数。

typescript 复制代码
// 声明构造函数接口:接收可选 string 参数,返回 Error 实例
interface ErrorConstructor {
  new (message?: string): Error;
}

// 原生 Error 类符合该接口约束
const MyError: ErrorConstructor = Error;
const err = new MyError('出错了');

三、接口的继承

接口支持通过 extends 关键字继承其他类型,实现类型复用与扩展。继承的类型可以是接口type 定义的对象类型

1. 接口继承接口

这是最常见的继承场景,子接口会继承父接口的所有成员,并可添加新成员。

typescript 复制代码
// 父接口 Shape
interface Shape {
  name: string;
}

// 子接口 Circle 继承 Shape
interface Circle extends Shape {
  radius: number; // 新增属性
}

// 实现子接口:需包含 name 和 radius
const circle: Circle = {
  name: 'circle',
  radius: 10
};
多重继承

接口支持同时继承多个父接口,多个父接口用逗号分隔,子接口会合并所有父接口的成员。

typescript 复制代码
interface Style {
  color: string;
}

interface Shape {
  name: string;
}

// 多重继承:同时继承 Style 和 Shape
interface Circle extends Style, Shape {
  radius: number;
}

// 实现:需包含 color、name、radius
const circle: Circle = {
  color: 'red',
  name: 'circle',
  radius: 10
};
继承的冲突处理
  • 子接口与父接口的同名属性必须类型兼容,否则会报错。
  • 多重继承时,多个父接口的同名属性也必须类型兼容,否则会报错。
typescript 复制代码
interface Foo {
  id: string;
}

// 错误:子接口 Bar 的 id 类型(number)与父接口 Foo 的 id 类型(string)不兼容
interface Bar extends Foo {
  id: number;
}

interface Baz {
  id: number;
}

// 错误:父接口 Foo 和 Baz 的 id 类型冲突
interface Qux extends Foo, Baz {
  type: string;
}

2. 接口继承 type

接口可以继承 type 命令定义的对象类型 ,实现类型扩展。注意:若 type 定义的是非对象类型(如联合类型),接口无法继承。

typescript 复制代码
// type 定义对象类型 Country
type Country = {
  name: string;
  capital: string;
};

// 接口继承 type,新增 population 属性
interface CountryWithPop extends Country {
  population: number;
}

const china: CountryWithPop = {
  name: '中国',
  capital: '北京',
  population: 1400000000
};

3. 接口继承类

接口可以继承类,会继承该类的所有成员(包括实例属性和方法),但不包括类的实现。

typescript 复制代码
class A {
  x: string = '';
  y(): boolean {
    return true;
  }
}

// 接口 B 继承类 A,新增 z 属性
interface B extends A {
  z: number;
}

// 实现接口 B:需包含 x、y 方法、z
const b: B = {
  x: 'hello',
  y: () => true,
  z: 123
};
注意事项

如果被继承的类包含私有成员(private)保护成员(protected),接口虽然可以继承,但无法用于对象实现(因为对象无法拥有私有/保护成员),只能被其他类实现(且需继承该父类)。

四、接口合并

TypeScript 的一个独特特性:多个同名接口会自动合并为一个接口 。这一特性非常适合扩展第三方库的类型(如为 window 对象添加自定义属性)。

1. 基本合并规则

同名接口的属性会合并,方法会形成函数重载。

typescript 复制代码
// 第一个 Box 接口
interface Box {
  height: number;
  width: number;
}

// 第二个 Box 接口
interface Box {
  length: number;
}

// 合并后的 Box 接口:包含 height、width、length
const box: Box = {
  height: 10,
  width: 20,
  length: 30
};

2. 合并的冲突处理

  • 同名属性的类型必须完全一致,否则会报错。
  • 同名方法会形成函数重载 ,且后定义的接口方法优先级更高,排在重载列表的前面。
typescript 复制代码
// 错误:同名属性 a 的类型冲突(number vs string)
interface A {
  a: number;
}
interface A {
  a: string;
}

// 方法合并形成函数重载
interface Cloner {
  clone(animal: Animal): Animal;
}
interface Cloner {
  clone(animal: Sheep): Sheep;
}
interface Cloner {
  clone(animal: Dog): Dog;
  clone(animal: Cat): Cat;
}

// 合并后等同于
interface Cloner {
  clone(animal: Dog): Dog;
  clone(animal: Cat): Cat;
  clone(animal: Sheep): Sheep;
  clone(animal: Animal): Animal;
}
特殊优先级:字面量类型参数

如果函数重载的参数是字面量类型,该方法会拥有最高优先级,排在重载列表的最前面,不受定义顺序影响。

typescript 复制代码
interface A {
  f(x: any): void;
}
interface A {
  f(x: 'foo'): boolean;
}

// 合并后:字面量类型参数的方法排在前面
interface A {
  f(x: 'foo'): boolean;
  f(x: any): void;
}

3. 接口合并的实战场景

最常见的用途是扩展全局对象的类型 ,比如为浏览器的 window 对象添加自定义属性。

typescript 复制代码
// 扩展 Document 接口,添加 foo 属性
interface Document {
  foo: string;
}

// 现在可以安全地使用 document.foo,不会报错
document.foo = 'hello';

五、接口联合类型的同名属性

当两个接口组成联合类型时,如果存在同名属性,该属性的类型会自动合并为联合类型

typescript 复制代码
interface Circle {
  area: bigint;
}

interface Rectangle {
  area: number;
}

// 联合类型:Circle | Rectangle
declare const shape: Circle | Rectangle;

// 同名属性 area 的类型为 bigint | number
const area: bigint | number = shape.area;

六、interface 与 type 的异同

interfacetype 都可以描述对象类型,在很多场景下可以互换,但两者存在核心区别,适用于不同的使用场景。

1. 相同点

  • 都可以描述对象类型,约定属性和方法的类型。
  • 都支持扩展:interface 通过 extends 扩展,type 通过交叉类型 & 扩展。
typescript 复制代码
// interface 扩展
interface Animal {
  name: string;
}
interface Bear extends Animal {
  honey: boolean;
}

// type 扩展(交叉类型)
type AnimalType = {
  name: string;
};
type BearType = AnimalType & {
  honey: boolean;
};

2. 核心区别

特性 interface type
支持的类型 仅支持对象类型(包括数组、函数) 支持任意类型(对象、原始类型、联合类型、交叉类型等)
扩展方式 通过 extends 关键字继承 通过交叉类型 & 实现扩展
声明合并 支持同名接口自动合并 不支持同名类型,重复定义会报错
属性映射 不支持映射类型 支持映射类型(如 [Key in keyof T]
this 关键字 支持在方法中使用 this 不支持在对象类型中使用 this

3. 选型建议

  • 优先使用 interface:当需要描述对象类型,且可能需要扩展或合并时(如组件 props、API 响应数据结构)。
  • 优先使用 type:当需要描述非对象类型(如联合类型、原始类型),或需要使用映射类型时。

七、核心总结

  1. 接口的本质:是对象类型的模板,约定对象的结构与类型,实现该接口的对象必须严格遵循约定。
  2. 接口的成员:支持对象属性、属性索引、方法、函数、构造函数五种形式,覆盖对象的所有常见语法。
  3. 接口的继承:可继承接口、type 对象类型、类,多重继承需注意属性类型兼容性。
  4. 接口合并:同名接口自动合并,属性合并、方法形成重载,适合扩展第三方类型。
  5. 与 type 的区别:interface 专注对象类型,支持继承和合并;type 更灵活,支持任意类型。
相关推荐
AI_56782 小时前
Webpack从“配置到提速”,4步解决“打包慢、体积大”问题
前端·javascript·vue.js
Aotman_2 小时前
Vue el-table 字段自定义排序
前端·javascript·vue.js·es6
我有一棵树2 小时前
Vite 7 中 dev 没样式、build 却正常:一次由 CSS import 位置引发的工程化问题
前端·javascript·vue.js
懒大王、3 小时前
Vue dcm文件预览
前端·javascript·vue.js·dcm·cornerstone.js
梵得儿SHI3 小时前
Vue 高级特性:组件高级用法(动态组件、异步组件、组件缓存 keep-alive)
前端·javascript·vue.js·keep-alive·异步组件·动态组件·vue组件高级特性
EndingCoder3 小时前
泛型类和高级用法
linux·运维·前端·ubuntu·typescript
ℋᙚᵐⁱᒻᵉ鲸落3 小时前
【Vue3】Element Plus 表单显示自定义校验错误
前端·javascript·vue.js
切糕师学AI3 小时前
Vue 中的响应式布局
前端·javascript·vue.js
晷龙烬3 小时前
Vue组件使用三步走:创建、注册、使用(Vue2/Vue3双版本详解)
前端·javascript·vue.js