深入探究 TypeScript 中 declare、interface 与 type 的区别

82 阅读4分钟

引言

在 TypeScript 的世界里,declareinterface 和 type 是用于类型定义的重要工具。理解它们之间的区别,对于编写高质量、可维护的 TypeScript 代码至关重要。本文将深入探讨这三者的特性、差异,并通过实际案例帮助你更好地掌握它们的用法。

interface:类型的结构化定义

基础定义

interface 用于定义对象类型的结构。它描述了对象应该具有哪些属性以及这些属性的类型。例如,定义一个简单的用户对象类型:

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

let user: User = {
    name: "John",
    age: 30
};

扩展与合并

interface 具有可扩展性,可以通过声明同名 interface 来合并属性。

interface User {
    email: string;
}

// 此时 User 接口拥有 name, age, email 属性
let user: User = {
    name: "John",
    age: 30,
    email: "john@example.com"
};

应用场景

通常用于定义对象的形状,比如 API 响应数据的结构、函数参数的类型等。在大型项目中,interface 有助于团队成员之间对数据结构达成共识。

type:类型别名的灵活运用

基础定义

type 用于创建类型别名,可以为任意类型(包括基本类型、联合类型、交叉类型等)创建一个新的名称。

// 为基本类型创建别名
type MyString = string;
let str: MyString = "Hello";

// 联合类型别名
type StringOrNumber = string | number;
let value: StringOrNumber = 10;
value = "world";

// 交叉类型别名
type Admin = { name: string; age: number };
type Role = { role: string };
type AdminWithRole = Admin & Role;
let admin: AdminWithRole = { name: "Alice", age: 25, role: "admin" };

与 interface 的区别

  • 功能覆盖type 比 interface 更灵活,能表示一些 interface 无法表示的类型,如联合类型、交叉类型。
  • 重复定义interface 可以重复定义并合并,而 type 重复定义会报错。

应用场景

适用于需要对复杂类型进行简化、创建联合或交叉类型别名的场景。在处理函数重载时,type 别名也能使代码更简洁。

declare:声明外部类型

解析

declare 关键字用于声明存在但未在当前作用域定义的变量、函数、类型等。它主要用于告诉 TypeScript 编译器,某个类型或变量在其他地方定义,编译时不会报错。这在引入第三方库或处理全局变量时非常有用。

声明全局变量

假设在 HTML 页面中有一个全局变量 myGlobal,在 TypeScript 中使用它之前需要声明:

declare var myGlobal: string;
console.log(myGlobal);

声明模块

当使用一个没有 TypeScript 类型定义的 JavaScript 模块时,可以使用 declare module 来声明模块的形状。

declare module 'custom - module' {
    export function customFunction(): void;
}

import { customFunction } from 'custom - module';
customFunction();

与 interface 和 type 的关系

declare 本身并不定义类型,而是声明类型的存在。它可以与 interface 或 type 结合使用。例如,声明一个全局的 User 接口:

declare global {
    interface User {
        address: string;
    }
}

let user: User = {
    name: "Bob",
    age: 28,
    address: "123 Main St"
};

案例分析

场景:创建一个图形绘制库

假设我们正在开发一个图形绘制库,需要定义不同图形的类型。

使用 interface

interface Point {
    x: number;
    y: number;
}

interface Circle {
    type: "circle";
    center: Point;
    radius: number;
}

interface Rectangle {
    type: "rectangle";
    topLeft: Point;
    width: number;
    height: number;
}

function drawShape(shape: Circle | Rectangle) {
    if (shape.type === "circle") {
        console.log(`Drawing a circle at (${shape.center.x}, ${shape.center.y}) with radius ${shape.radius}`);
    } else {
        console.log(`Drawing a rectangle at (${shape.topLeft.x}, ${shape.topLeft.y}) with width ${shape.width} and height ${shape.height}`);
    }
}

let circle: Circle = {
    type: "circle",
    center: { x: 100, y: 100 },
    radius: 50
};

drawShape(circle);

使用 type

type Point = {
    x: number;
    y: number;
};

type Circle = {
    type: "circle";
    center: Point;
    radius: number;
};

type Rectangle = {
    type: "rectangle";
    topLeft: Point;
    width: number;
    height: number;
};

type Shape = Circle | Rectangle;

function drawShape(shape: Shape) {
    if (shape.type === "circle") {
        console.log(`Drawing a circle at (${shape.center.x}, ${shape.center.y}) with radius ${shape.radius}`);
    } else {
        console.log(`Drawing a rectangle at (${shape.topLeft.x}, ${shape.topLeft.y}) with width ${shape.width} and height ${shape.height}`);
    }
}

let circle: Circle = {
    type: "circle",
    center: { x: 100, y: 100 },
    radius: 50
};

drawShape(circle);

使用 declare

假设我们引入了一个第三方的图形渲染库,它提供了一些全局函数,但没有 TypeScript 类型定义。

declare function renderShape(shape: any): void;

// 假设我们已经定义了 Circle 和 Rectangle 类型
let circle: Circle = {
    type: "circle",
    center: { x: 100, y: 100 },
    radius: 50
};

renderShape(circle);

在这个案例中,interface 和 type 都很好地定义了图形的类型结构,type 在表示联合类型 Shape 时更加直观。而 declare 则解决了引入外部无类型定义库的问题。

总结

  • interface 专注于定义对象的结构,可扩展和合并,适用于对象类型的定义。

  • type 提供了更灵活的类型别名创建方式,能表示复杂类型,如联合和交叉类型。

  • declare 用于声明外部存在的类型或变量,帮助 TypeScript 与现有 JavaScript 代码或第三方库集成。

熟练掌握这三个关键字的区别和用法,将使你在 TypeScript 开发中更加得心应手,编写出健壮、可维护的代码。希望本文能帮助你深入理解它们,并在实际项目中灵活运用。