Type vs Interface:读完这篇就没有面试官能难倒你了

Type vs Interface:读完这篇就没有面试官能难倒你了

引言

在 TypeScript 中,typeinterface 是日常开发里最高频出现的两个关键词。它们都能描述对象的结构,但底层机制有本质区别。面试官最爱问的 TS 问题,十有八九就是这个。本文将从一个 React 函数组件 的实际场景出发,结合笔记和代码,把它们的异同一次性讲透。


一、共同点:都能描述"对象的形状"

先看一段最基础的代码:

ini 复制代码
// interface 写法
interface User {
  name: string;
  age: number;
  avatarUrl: string;
}

// type 写法
type UserType = {
  name: string;
  age: number;
  avatarUrl: string;
};

const u1: User = { name: "张三", age: 18, avatarUrl: "https://example.com/avatar.jpg" };
const u2: UserType = { name: "李四", age: 20, avatarUrl: "https://example.com/avatar.jpg" };

共同之处在于interfacetype 都可以:

  • 描述一个对象的结构------包含哪些属性,每个属性是什么类型
  • 用于函数参数的类型注解
  • 用于函数返回值类型
  • 给变量、对象做类型约束

它们在 对象形状描述 这个层面,语法不同但能力等价。


二、区别一:继承机制完全不同

这是一个底层差异------两者实现"继承/组合"的方式截然不同。

ini 复制代码
interface Person {
  name: string;
}

// interface 用 extends 继承,像 Java 的面向对象
interface Employee extends Person {
  job: string;
}

// type 用交叉类型 & 组合
type PersonType = { name: string };
type EmployeeType = PersonType & { job: string };

const e1: Employee = { name: "张三", job: "字节开发工程师" };
const e2: EmployeeType = { name: "李四", job: "大厂的苗子" };

关键理解

interface type
语法 extends(继承) &(交叉类型)
语义 "我是一个 Person,并且多了 job" "把 PersonType 的所有属性 和 { job } 合并"
背后 面向接口编程的 名义类型 思路 类型层面的 结构运算

extends 背后是 TypeScript 对传统 OOP 的致敬------它显式声明了 Employee Person 的子类型。而 & 是一种类型代数运算,它不关心"谁是谁的子类型",只是把两个类型的属性集合做并集


三、区别二:声明合并------interface 的"按揭声明"能力

这是 interface 最独特的能力,也是面试的高频考点:

typescript 复制代码
interface Animal {
  name: string;
}

// 同名 interface 会自动合并!
interface Animal {
  age: number;
}

const dog: Animal = { name: "三寸钉子", age: 2 }; // ✅ 两个属性都必须有

// type 呢?
type AnimalType = { name: string };
type AnimalType = { age: number }; // ❌ 报错!标识符重复

为什么会这样?

TypeScript 编译器对 interface 做了特殊处理------遇到同名声明时,会自动合并成员 而非视为重复定义。这叫 Declaration Merging(声明合并)

这个设计的价值在于:

typescript 复制代码
场景:你引用了一个第三方库的类型定义,想在不修改源码的情况下补充属性。

第三方 .d.ts:
  interface Window { appName: string }

你的代码:
  interface Window { appVersion: number }  // 合并,不报错!

使用时:
  window.appName     // ✅
  window.appVersion  // ✅

type 不行,因为 type 本质上是一个类型别名 ------对已有类型的引用。同一个作用域内,同一个名字不能指向两个不同的东西,这和 const 不可重复声明是同一个道理。


四、区别三:type 能表示的东西 interface 做不到

这是 type 的不可替代之处------它能描述非对象类型

typescript 复制代码
// 联合类型 ------ interface 做不到
type ID = string | number;

// 元组类型 ------ interface 做不到
type Point = [number, number];

// interface 试一下?
interface ID {}  // 空的,完全无法表达 "string 或 number" 这个语义
typescript 复制代码
          interface 能表达的          type 能表达的
        ┌─────────────────┐    ┌───────────────────────┐
        │   对象结构        │    │   对象结构              │
        │   { a: string }  │    │   { a: string }        │
        │                  │    │                        │
        └─────────────────┘    │   联合类型 string|number │
                               │   元组     [number,number]│
                               │   字面量   'a'|'b'      │
                               │   映射类型 等等...        │
                               └───────────────────────┘

理解本质type类型别名 ,它可以指向任意类型表达式------对象类型、联合类型、元组、原始类型、字面量类型......而 interface 的设计目的就是定义对象的契约,它天然只能描述对象形状。


五、区别四:函数类型------都能写,type 更简洁

typescript 复制代码
// interface 定义函数类型 ------ 需要写完整的方法签名
interface AddFN {
  (a: number, b: number): number;
}
const add1: AddFN = (x, y) => x + y;

// type 定义函数类型 ------ 像箭头函数一样自然
type AddType = (a: number, b: number) => number;

两者都能表达函数类型,但 type 的语法更接近日常写函数的方式,理解成本更低。interface 则需要写带调用签名的对象------它依然是把函数当作"可调用的对象"来描述,这是 OOP 语系下的思维惯性。


六、实战:React 函数组件里为什么首选 interface

回到实际工程。在 React + TypeScript 项目中,组件 Props 几乎都是用 interface 定义的

typescript 复制代码
// 接口 ------ 传统 OOP 核心概念:抽象
// JS 本身是原型式的、函数式一等公民
// TS 为大型企业级开发提供强类型,思路偏 Java 传统 OOP
// class extends / implements interface
// 面向接口编程 ------ 父子组件之间的数据契约

interface User {
  name: string;
  age: number;
  avatarUrl: string;
}

interface UserCardProps {
  user: User;
  onEdit: (id: number) => void;
}

const UserCard: React.FC<UserCardProps> = ({ user, onEdit }) => {
  return (
    <div>
      <img src={user.avatarUrl} alt={user.name} />
      <span>{user.name} · {user.age}岁</span>
      <button onClick={() => onEdit(user.id)}>编辑</button>
    </div>
  );
};

为什么函数组件的 Props 要选 interface?

  1. 语义对齐 :Props 天然是"契约"------父组件说"我提供这些数据",子组件说"我需要这些数据"。interfaceextends 继承和 OOP 的接口编程一脉相承,表达的正是这个"契约"概念。

  2. 可扩展性 :用 interface 定义的 Props 可以被 extends 扩展出更具体的变体,这和多态组件模式天然匹配:

    kotlin 复制代码
    interface BaseCardProps { user: User }
    interface AdminCardProps extends BaseCardProps { permissions: string[] }
  3. 声明合并 :第三方组件库的 Props 类型,你可以通过 interface 的同名合并能力无缝扩展,而不用改库代码。

  4. 官方与社区惯例 :React 类型定义本身(React.FCReact.ComponentProps 等)大量使用 interface,社区主流项目(Ant Design、Material UI)的组件 Props 也清一色 interface。跟社区走,可读性最高。


七、一张图总结

go 复制代码
                        选择指南
                          │
         ┌────────────────┼────────────────┐
         │                │                │
    需要描述对象形状?   需要联合/元组?   不确定?
         │                │                │
    ┌────▼────┐      ┌────▼────┐      ┌────▼────┐
    │interface│      │  type   │      │interface│
    │         │      │         │      │ (默认)   │
    │ React   │      │ 工具类型│      │ 可扩展性│
    │ Props   │      │ 灵活组合│      │ 更友好  │
    │ 契约设计│      │ 类型体操│      │         │
    └─────────┘      └─────────┘      └─────────┘

一句话记忆

interface 定义"契约"(对象能做什么),type 创造"别名"(这个东西叫什么)。写组件 Props 用 interface,写工具类型用 type

两者既互补又互有交叉,理解它们的底层差异,你才能在代码评审时说清楚"这里为什么选这个"------而不只是"我看别人都这么写"。

相关推荐
东风破_4 小时前
danci 项目(三):从 JSON 数据到 AI Coding,真实项目里的数据清洗、Prompt 和工程规范
前端·后端·node.js
怕浪猫4 小时前
FDE 最大的浪费不是写出有 bug 的代码,而是漂亮地解决了一个错误的问题
面试·架构·github
东风破_4 小时前
danci 项目(一):从需求到架构,一个单词学习系统为什么会这样设计
前端·后端·node.js
蒲公英eric4 小时前
从客户端到服务端:DVWA DOM 型 XSS 模块完整漏洞分析教程
前端·web安全·ai·xss·dvwa·ai安全
单线程_015 小时前
从案例分析 Vue3 Tokenizer+Parser 源码三
前端·javascript·vue.js
小磊哥er5 小时前
深入解构Claude Code - 第 10 篇 · 高级能力
typescript·ai编程
顶点多余5 小时前
那些在算法中适合巩固的知识点---1
java·前端·算法
Setsuna_F_Seiei5 小时前
前端转型 Agent 开发 03 之 Agent Tools - 给 Agent 装上手脚
前端·agent·ai编程
jay神5 小时前
【计算机毕业设计】基于SpringBoot的程序教学辅助系统
java·前端·vue.js·spring boot·后端·毕业设计·课程设计
小磊哥er6 小时前
深入解构Claude Code - 第 9 篇 · 怎么给它加功能
typescript·ai编程