MST

星途 面试题库

面试题:TypeScript 接口与类型别名的深度对比及应用场景

详细阐述 TypeScript 中接口(interface)和类型别名(type alias)的区别,包括但不限于声明方式、功能特性、可扩展性等方面。给出至少三个实际项目场景,说明在这些场景中为什么选择接口或者类型别名。同时,在一个复杂的 React 组件开发场景下,使用接口和类型别名分别定义组件的 props 类型,并分析哪种方式更优及其原因。
14.1万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

声明方式

  • 接口(interface):使用 interface 关键字声明。例如:
interface User {
  name: string;
  age: number;
}
  • 类型别名(type alias):使用 type 关键字声明。例如:
type User = {
  name: string;
  age: number;
};

功能特性

  • 接口(interface)
    • 主要用于定义对象的形状,对对象的结构进行描述。
    • 支持声明合并,即多个同名接口声明会自动合并。例如:
interface User {
  name: string;
}
interface User {
  age: number;
}
// 此时 User 接口有 name 和 age 属性
  • 类型别名(type alias)
    • 不仅可以用于定义对象类型,还能定义基本类型别名、联合类型、交叉类型等。例如:
type StringOrNumber = string | number;
type UserInfo = { name: string } & { age: number };
  • 不支持声明合并。

可扩展性

  • 接口(interface):扩展性较好,通过声明合并可以在不同地方为同一个接口添加新属性。例如在大型项目中,不同模块可能需要对同一个基础接口进行扩展。
  • 类型别名(type alias):一旦定义,无法像接口那样通过声明合并进行扩展,如需扩展,需重新定义新的类型别名。

实际项目场景

  1. 对象形状定义
    • 选择接口:在定义 API 响应数据的结构时,例如定义用户信息接口。因为 API 响应数据结构相对固定,且可能在不同模块有扩展需求,接口的声明合并特性方便扩展。
interface UserResponse {
  id: number;
  name: string;
}
// 其他模块可扩展
interface UserResponse {
  email: string;
}
  • 选择类型别名:如果只是简单定义一个一次性使用的对象类型,例如函数内部使用的临时对象类型。使用类型别名定义更简洁。
function printUser(user: { name: string; age: number }) {
  console.log(user);
}
// 也可写成
type User = { name: string; age: number };
function printUser(user: User) {
  console.log(user);
}
  1. 联合类型与交叉类型
    • 选择类型别名:在定义函数参数可以接受多种类型时,使用类型别名定义联合类型很方便。例如一个函数可以接受字符串或数字作为参数。
type StringOrNumber = string | number;
function printValue(value: StringOrNumber) {
  console.log(value);
}
  1. 基本类型别名
    • 选择类型别名:为基本类型定义别名,提高代码可读性。比如在处理颜色值时,定义一个 Color 类型别名。
type Color = string;
function setColor(color: Color) {
  // 设置颜色逻辑
}

React 组件 props 类型定义

  • 使用接口定义 props 类型
import React from 'react';

interface ButtonProps {
  text: string;
  onClick: () => void;
  disabled?: boolean;
}

const Button: React.FC<ButtonProps> = ({ text, onClick, disabled = false }) => {
  return (
    <button disabled={disabled} onClick={onClick}>
      {text}
    </button>
  );
};

export default Button;
  • 使用类型别名定义 props 类型
import React from 'react';

type ButtonProps = {
  text: string;
  onClick: () => void;
  disabled?: boolean;
};

const Button: React.FC<ButtonProps> = ({ text, onClick, disabled = false }) => {
  return (
    <button disabled={disabled} onClick={onClick}>
      {text}
    </button>
  );
};

export default Button;

分析哪种方式更优及其原因

在这个简单的 React 组件场景下,接口和类型别名都能很好地完成任务。但如果考虑项目规模和未来扩展性:

  • 接口更优:如果项目规模较大,并且预计 ButtonProps 可能在不同模块有扩展需求,接口的声明合并特性可以让代码更易于维护。例如后续可能需要添加 loading 属性,在不同模块通过声明合并就能轻松实现。
  • 类型别名更优:如果项目规模较小,并且 ButtonProps 不会有扩展需求,类型别名定义相对更简洁,且它能灵活定义联合类型等特性,在某些复杂类型需求场景下有优势。 总的来说,没有绝对的优劣,需根据项目实际情况选择。