type 和 interface 有以下几点不同(TS)

在TypeScript中,typeinterface 都是用来定义类型的构造,尽管它们在某些方面有所不同,但在大多数情况下可以互换使用来描述对象结构。以下是两者之间主要的区别点:

继承和合并:扩展和更新:范围和可见性:

1. 继承和合并:

    • interface 支持多重继承,可以使用 extends 关键字从多个接口继承成员。
    • type 也可以实现类似的功能,但不是通过继承而是通过交叉类型(& 符号)实现。不过,type 不支持声明合并,这意味着在同一作用域内无法多次定义同一个类型别名。
  • interface 在 TypeScript 中支持多重继承,允许一个接口通过 extends 关键字从一个或多个其他接口中继承属性和方法。例如:

interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

interface Cat extends Animal {
  color: string;
}

// 多重继承
interface SpecialDog extends Dog, Cat {
  canFetch: boolean;
}

  • 类似地,type 也可以通过交叉类型(Intersection Type)来模拟多重继承的效果,即将多个类型合并成一个新的类型,新类型包含所有参与交叉类型的成员。但需要注意的是,这并不是严格意义上的“继承”,而是类型合并:

type Animal = {
  name: string;
}

type Dog = {
  breed: string;
}

type Cat = {
  color: string;
}

// 使用交叉类型实现类似多重继承的效果
type SpecialDog = Dog & Cat & {
  canFetch: boolean;
}

同时强调一点,正如前面所述,type 不支持声明合并,所以在同一作用域内多次定义同名的类型别名是不允许的,会产生编译错误。而在同一程序的不同地方多次声明同一接口(interface),TypeScript 会自动合并这些声明,形成一个包含所有声明成员的接口。

2. 扩展和更新:

    • interface 在不同的文件或模块中可以被多次声明并且自动合并其成员。
    • type 则不允许这样做,重复定义会导致错误。

  • interface 具有声明合并的特性。在不同的文件或模块中,可以多次声明同一个接口,并且 TypeScript 编译器会自动合并所有声明中的成员。这对于大型项目或库的设计非常有用,可以将接口分散在多个模块中定义,保持代码组织清晰的同时也能确保类型完整性。

例如,在 file1.tsfile2.ts 中分别定义同一个接口:

file1.ts

export interface User {
  id: number;
  name: string;
}

file2.ts

export interface User {
  email: string;
  role: string;
}

在主文件中导入合并后的接口:

import * as file1 from './file1';
import * as file2 from './file2';

// TypeScript 会自动合并 User 接口的定义
let user: file1.User & file2.User = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  role: 'admin',
};

  • 相反,type 一旦被定义,就不能在同一作用域或模块中再次定义。若试图在不同位置重复定义同一个类型别名,TypeScript 编译器会抛出错误,认为这是一个重复声明。

3. 类型表达能力:

    • type 更灵活,可以用来定义更复杂的类型构造,比如:
      • 元组类型(Tuple types)
      • 联合类型(Union types)
      • 泛型类型别名(Generic Type Aliases)
      • 字面量类型(Literal types)
      • 类型断言(Type Assertions)
      • 以及通过 typeof 获取变量或表达式的类型等。
    • interface 不可以直接定义这些复杂的类型,但是可以通过内部类型(intrinsic types)间接地组合出类似的结构。
    • 您提供的信息非常准确:

  • type 在 TypeScript 中确实提供了更大的灵活性,允许定义多种复杂的类型构造:
    • 元组类型(Tuple types):用于表示固定数量和类型的数组。
type Coordinates = [number, number]; // 表示一个包含两个 number 类型元素的数组

    • 联合类型(Union types):表示取值可以是几种类型之一。
type ApiResponse = string | number | undefined; // 表示可能是 string、number 或 undefined

    • 泛型类型别名(Generic Type Aliases):创建一个可以接受类型参数的类型别名。
type Box<T> = { value: T }; // Box 是一个接受泛型 T 的类型别名

    • 字面量类型(Literal types):用于表示具体的值而不是一个值的类型。
type Status = 'success' | 'failure'; // 只能是 'success' 或 'failure'

    • 类型断言(Type Assertions):在表达式级别上临时指定一个类型,但这并非 type 关键字直接定义,而是通过语法 (expression as Type)<Type>expression 使用。
    • typeof 获取变量或表达式的类型:通过 typeof 关键字可以获取变量或表达式的静态类型。
let someVar: string = 'Hello';
type VarType = typeof someVar; // VarType 等价于 string

  • 虽然 interface 本身不直接支持上述所有类型的定义,但它可以通过组合内置类型(如数组、对象类型等)和其他接口来间接地创建类似结构。例如,可以定义一个包含元组类型的接口属性,或者一个使用泛型约束的接口等。不过,interface 无法直接用于定义字面量类型或使用 typeof 关键字这样的类型操作。

4. 类型声明性质:

    • type 可以为任何类型创建一个别名,包括基础类型和复杂类型。
    • interface 则主要用于描述对象结构,且它实际上会创建一个新的类型,而不是简单的别名。

  • type关键字允许我们创建类型别名,这意味着它可以为现有的任何类型创建一个新的名称,包括基本类型(如 stringnumberboolean 等)和复杂的复合类型(如元组、联合类型、映射类型等)。例如:
type Name = string;
type Point = [number, number];
type NullableString = string | null;

  • interface则是用于定义对象的结构,即描述对象所具有的属性和方法。当我们定义一个接口时,TypeScript实际上是创建了一个新的类型,这个类型包含了接口中定义的所有属性和方法。例如:
interface User {
  id: number;
  name: string;
  email: string;
  isPremium: boolean;
  login(): void;
}


虽然在某些情况下,typeinterface可以达到类似的效果(例如定义对象结构),但它们在功能和应用场景上还是有显著差异的。特别是在处理复杂类型构造和类型合并时,这两个关键字的选择和使用会有一定的区别。

5. 范围和可见性:

    • 早期版本的 TypeScript 中,type 定义的作用域仅限于当前文件(模块),而 interface 可以跨越文件(模块)边界。
    • 自从引入了 exportimport ,两者在模块系统中的可见性和作用域现在更加相似,都可以在模块间共享。
    • 现在在支持模块系统的现代 TypeScript 版本中,typeinterface 都可以通过 exportimport 关键字在不同模块间共享和使用。下面给出实际例子:

moduleA.ts

// 导出一个类型别名
export type Name = string;

// 导出一个接口
export interface User {
  id: number;
  name: Name;
  email: string;
}

moduleB.ts

// 导入 moduleA 中的类型别名和接口
import { Name, User } from './moduleA';

// 使用导入的类型别名和接口定义变量和函数
let userName: Name = "Alice";

function logUser(user: User) {
  console.log(`User ID: ${user.id}, Name: ${user.name}, Email: ${user.email}`);
}

// 创建一个 User 类型的对象
let user: User = {
  id: 1,
  name: userName,
  email: "alice@example.com"
};

logUser(user);

在这个例子中,moduleB 成功地导入了 moduleA 中定义的 Name 类型别名和 User 接口,并在 moduleB 中正常使用。这就证明了无论 type 还是 interface,都能够在 TypeScript 的模块系统中跨文件(模块)共享。

6. 函数表达式类型:

    • type 可以用来定义函数表达式的类型,包括那些包含 call、construct 和 other function members 的类型。
    • interface 也能描述带有可调用签名的对象类型,但对于函数表达式的细节描述上,type 更加直接。
    • 两者均可用来定义函数类型,但语法上 type 通常更为简洁直观,例如:
type AddFn = (x: number, y: number) => number;

同样也可以通过 interface 定义,但是稍微复杂:

interface AddFn {
  (x: number, y: number): number;
}

根据实际应用场景,开发者可以选择更适合的方式来定义类型。当需要定义简单对象结构时,两者几乎可以随意选择;而在处理较复杂的类型构造或需要多重继承特性时,可能会倾向于使用 interface 或特定情况下的 type。随着 TypeScript 的发展,一些早期存在的差异可能会因为新特性的引入而有所变化。

开源协议

GPL:

​ 不允许修改后和衍生的代码作为闭源的商业软件发布和销售

​ 例子:Linux

MIT:

​ 目前限制最少的协议,唯一条件是必须包含原作者的许可信息

​ 例子:jQuery、Node.js

  • 11
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向画

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值