TypeScript 接口

interface 是 TS 里描述"对象形状"最常用的工具——它定义一个对象应该有哪些字段、字段是什么类型。前后端约定的数据结构、函数的参数对象、组件的 props,几乎都用 interface 描述。学会它,你就能给任意复杂的对象定义类型。

1. 基本用法

interface 关键字声明一个接口,里面列出字段名和类型。然后用它约束变量或参数:

// interface 描述"一个对象应该长什么样"
interface User {
  id: number;
  name: string;
  age: number;
}

// 用 interface 约束变量/参数的类型
const u: User = { id: 1, name: "小明", age: 20 };

function greet(user: User): string {
  return `你好, ${user.name}!`;
}
console.log(greet(u));   // 你好, 小明!

// ❌ 少写字段会报错
const bad: User = { id: 2, name: "小红" };
// 报错:缺少属性 "age"

// ❌ 多写字段也会报错
const bad2: User = { id: 3, name: "小刚", age: 21, extra: 1 };
// 报错:对象文字可以只指定已知属性,"extra" 不存在

TS 会强制检查:字段数量必须完全一致(不能多也不能少),类型也必须匹配。这种"结构必须吻合"的检查,是 TS 治病救人的主战场。

2. 可选属性:加问号

有些字段不一定出现,比如用户的昵称、文章的标签。在属性名后加 ? 表示可选

// 可选属性:在属性名后加 ?
interface Article {
  title: string;
  author?: string;        // 可选,可以没有
  tags?: string[];        // 可选
}

const a1: Article = { title: "标题" };                 // 合法
const a2: Article = { title: "标题", author: "小明" }; // 也合法

// 注意:访问可选属性时,值可能是 undefined
const article: Article = { title: "t" };
console.log(article.author.length);
// ❌ TS 提醒:author 可能是 undefined
// 正确做法:先判空或用可选链
console.log(article.author?.length);   // undefined(不报错)

注意一个重要细节:访问可选属性时,TS 知道它可能是 undefined,会强制你先处理空值。用可选链 ?. 是最简洁的写法。这一点能挡住大量"undefined is not a function"的经典 bug。

3. 只读属性:readonly

有些字段创建后就不该再改(比如 id、创建时间)。用 readonly 修饰,赋值后任何修改都会报错:

// 只读属性:readonly 修饰,赋值后不能再改
interface Point {
  readonly x: number;
  readonly y: number;
}

const p: Point = { x: 1, y: 2 };
p.x = 3;
// ❌ 报错:无法分配到 "x",因为它是只读属性

// readonly 只在编译期检查,运行时并不真正阻止
// (和 const 类似,但 const 作用于变量,readonly 作用于属性)

4. 描述函数类型

interface 不只能描述对象,还能描述函数的签名(参数和返回值)。不过函数类型更多用 type 别名写(下一章讲),这里了解即可:

// 用 interface 描述函数类型
interface Greet {
  (name: string, age: number): string;
}

// 等价于 type 形式: type Greet = (name: string, age: number) => string
const greet: Greet = (name, age) => {
  return `你好 ${name}, ${age} 岁`;
};
// 注意:参数 name/age 的类型由 interface 推断,不用再写

// 函数类型的 interface 不算常用,更多用 type 别名(下一章)

5. 接口继承:extends

接口可以继承其他接口,复用已有定义。interface B extends A 表示 B 拥有 A 的全部字段,再加自己的:

// 接口可以继承(扩展)其他接口
interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;        // Dog 拥有 name + breed
}

const d: Dog = { name: "旺财", breed: "金毛" };

// 一个接口可以继承多个接口(多继承)
interface Barker {
  bark(): void;
}
interface Dog2 extends Animal, Barker {
  breed: string;
}

// 接口还能"合并"(同名声明会自动合并字段)
interface Window { myProp: string; }   // 给浏览器 Window 加自定义属性

接口还能多继承(用逗号分隔),并且同名 interface 会自动合并字段——后者常用于给全局类型(如 Window)扩展自定义属性。

6. 索引签名:动态字段名

有时对象的字段名是不固定的(比如一个字典)。这时用索引签名

7. 命名规范

小结

interface 是 TS 描述对象形状的标准工具:必填属性直接写、可选加 ?、只读加 readonly、用 extends 继承。下一篇我们讲它的"兄弟"——类型别名 type,以及两者该怎么选。

← 上一篇 TypeScript 基础类型

下一篇 TypeScript 类型别名

✈️💬