TypeScript:比较Interface和Type

本文详细解析TypeScript中的Interface和Type,比较它们在声明对象类型、联合类型、类型别名、接口继承与实现上的异同,以及何时选择使用哪种方式,为开发者提供清晰的指导。

如果你用过TypeScript,一定接触过InterfaceType

5秒钟思考一下,他俩有什么相同和不同?

如果你对他们的不同一无所知,那么请继续往下看。

如果我们声明一个Point类型,可以通过以下两种方式都会达到我们想要的结果:

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

或者

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

上面的列子没有表现出 interfacetype 之间的不同。

让我们进一步探索。

TypeScript的类型包括原始数据类型、对象类型、高级类型等。

原始数据类型包括:boolean 、number、string、null、undefined 以及ES6中的新类型 Symbol 和ES10中的 BigInt。

高级类型:联合类型(Unions Type),泛型(Generics)

联合类型(Unions Type)

联合类型(Unions Type): 表示取值可以为多种类型中的一种。

举个栗子:

function printId(id: number | string) {
  console.log("Your ID is: " + id);
}
// OK
printId(101);
// OK
printId("202");
// Error
printId({ myID: 22342 });
//抛出错误信息 
//Argument of type '{ myID: number; }' is not assignable to parameter of type 'string | number'.
// Type '{ myID: number; }' is not assignable to type 'number'.
上面例子中如果删除传入一个对象 会抛出异常,不能把 '{ myID: number; }' 赋值给 string或者number类型。
function printId(id: number | string) {
  console.log(id.toUpperCase());
}

//Error
Property 'toUpperCase' does not exist on type 'string | number'.
  Property 'toUpperCase' does not exist on type 'number'.

我们来分析一下上面的报错,只有string类型的字符串有toUpperCase属性。number类型没有toUpperCase属性,ts类型推断的时候就会报错。感觉TypeScript很智能,可以提前感知错误。

如果想支持参数 既是 string类型,又是number类型,需要改进一下。如下:

function printId(id: number | string) {
  if (typeof id === "string") {
    // In this branch, id is of type 'string'
    console.log(id.toUpperCase());
  } else {
    // Here, id is of type 'number'
    console.log(id);
  }
}

类型别名(Type Aliases)

类型别名是指给一个类型起个新名字。注意这里不是定义一个新的类型,而是给了一个新的名字。

类型别名常用于 原始类型,联合类型(Unions Type)

type Point = {
  x: number;
  y: number;
};
// Exactly the same as the earlier example
function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}

printCoord({ x: 100, y: 100 });

联合类型

type ID = number | string;
type UserInputSanitizedString = string;

function sanitizeInput(str: string): UserInputSanitizedString {
  return "inputcontent"
}

类型别名是指给一个类型起个新名字,所以下面 stringUserInputSanitizedString都是相同类型

对象的类型—接口

在 TypeScript 中,我们使用接口(Interfaces)来定义对象类型。相比类型别名,Interfaces仅用于 对象类型。

继承—extend

interface 和 type 都支持继承,并且 interface 可以继承 type ,type又可以继承interface ,只是语法不一样。举例说明:

1.interface extend interface

interface PartialPointX { x: number; }
interface Point extends PartialPointX { y: number; }
2.interface extend type

type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }
type & type
type PartialPointX = { x: number; };
// 这里继承使用的是 & 
type Point = PartialPointX & { y: number; };
type & interface
interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };

实现—Implements

一个class可以实现(implement) interface 和 type,但不可以实现(implement)一个联合类型(unions type)

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

class SomePoint implements Point {
  x = 1;
  y = 2;
}

type AnotherPoint = {
  x: number;
  y: number;
};
class SomePoint2 implements AnotherPoint {
  x = 1;
  y = 2;
}

type PartialPoint = { x: number; } | { y: number; };
//不能实现一个联合类型,下面代码将会抛出异常
class SomePartialPoint implements PartialPoint {
  x = 1;
  y = 2;
}

声明合并—Declaration Merging

TypeScript在编译的时候,会将名字相同的interface合并成一个接口,但是 type 就不行 。

看一个简单的例子,interface名字一样,但是 属性名不同

interface Box {
  height: number;
  width: number;
}
interface Box {
  scale: number;
}
let box: Box = { height: 5, width: 6, scale: 10 };

上面的例子 把两个Box的类型合并成了下面结果

interface Box {
  height: number;
  width: number;
  scale: number;
}
let box: Box = { height: 5, width: 6, scale: 10 };

再来看一个例子,interface名字一样,但是 属性名也一样

interface Cloner {
  clone(animal: Animal): Animal;
}
interface Cloner {
  clone(animal: Sheep): Sheep;
}
interface Cloner {
  clone(animal: Dog): Dog;
  clone(animal: Cat): Cat;
}

三个 Cloner的interface 合并之后的结果如下:

interface Cloner {
  clone(animal: Dog): Dog;
  clone(animal: Cat): Cat;
  
  clone(animal: Sheep): Sheep;
  
  clone(animal: Animal): Animal;
}

仔细观察,合并之后,第三个接口的属性排在最前面,优先级最高。这个一定要注意

Interface和Type如何选择呢?

上面内容,我们比较了 InterfaceType 两者之间的相同点和不同点。接下来总结一下。

对于库中类型或第三方库类型,定义公共 API ,应使用接口来提供声明合并功能。

除此之外,我们随便选择,但是要保障代码的一致性。

参考资料

  • https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types
  • https://javascript.plainenglish.io/typescript-the-difference-between-interface-and-type-160fc318bb5c
TypeScript 中,`interface` `type` 都可以用于定义对象类型,但它们在语义使用场景上有一些关键区别。理解它们的异同可以帮助你写出更清晰、更安全的类型定义,特别是在 Vue 3 中定义 `props`、`emits`、`store` 等结构时尤为重要。 --- ### ✅ 一、基本用法对比 #### 1. `interface` ```ts interface User { id: number name: string } ``` #### 2. `type` ```ts type User = { id: number name: string } ``` 两者在定义对象结构时几乎完全相同,都可以被用来定义 Vue 组件的 `props` 类型、函数参数类型等。 --- ### ✅ 二、主要区别 | 特性 | `interface` | `type` | |------|-------------|--------| | **可扩展性(声明合并)** | ✅ 支持多次声明合并 | ❌ 不支持 | | **联合类型** | ❌ 不支持 | ✅ 支持 | | **元组、基本类型别名** | ❌ 不适合 | ✅ 支持 | | **映射类型、条件类型** | ❌ 有限支持 | ✅ 支持 | --- ### ✅ 三、详细对比说明 #### 1. 声明合并(Declaration Merging) `interface` 支持同名接口的多次声明,TypeScript 会自动合并它们: ```ts interface User { id: number } interface User { name: string } // 实际等价于: // interface User { // id: number // name: string // } ``` `type` 不支持这种行为,重复定义会报错: ```ts type User = { id: number } type User = { name: string } // ❌ Error: Duplicate identifier 'User' ``` #### 2. 联合类型(Union Types) `type` 可以使用联合类型来定义多个可能的类型: ```ts type ID = number | string type Result = 'success' | 'error' ``` `interface` 不支持联合类型。 #### 3. 类型别名 vs 接口(语义差异) - `type` 更适合用于**类型别名**、**联合类型**、**交叉类型**、**映射类型**等高级类型操作。 - `interface` 更适合用于描述**对象形状**,特别是需要被实现(`implements`)的类结构。 #### 4. 映射类型条件类型 `type` 支持更复杂的类型操作,例如: ```ts type PartialUser = Partial<User> type ReadonlyUser = Readonly<User> type MyType = T extends U ? true : false ``` 这些在 `interface` 中无法实现。 --- ### ✅ 四、Vue 3 使用建议 在 Vue 3 中定义组件的 `props`、`emits`、`store` 等类型时,推荐使用 `interface` 来描述对象结构,因为它更清晰、可读性强,并且可以被类实现(如组件逻辑抽离到类中时)。 ```ts interface User { id: number name: string } defineComponent({ props: { user: { type: Object as PropType<User>, required: true } }, setup(props) { // ... } }) ``` 而当你需要定义联合类型、映射类型、泛型等高级类型时,使用 `type` 更合适: ```ts type Theme = 'light' | 'dark' type StoreState = { user: User | null } ``` --- ### ✅ 五、总结对比表 | 特性 | `interface` | `type` | |------|-------------|--------| | 定义对象结构 | ✅ | ✅ | | 支持声明合并 | ✅ | ❌ | | 支持联合类型 | ❌ | ✅ | | 支持基本类型别名 | ❌ | ✅ | | 支持映射类型/条件类型 | ❌ | ✅ | | 更适合定义组件类型 | ✅ | ⚠️ | | 更适合高级类型操作 | ❌ | ✅ | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值