TypeScript type 和 interface 的区别

从使用的角度去看,这可能是个老生常谈的问题,就连 TypeScript 官方文档都会给你一个个介绍:

Interfaces vs Types in TypeScript

Documentation - Everyday Types

Learn about difference between Type & Interface in Typescript

具体来说(翻译成中文来说):

  1. 基本类型:用 type ,因为 interface 做不到

    type Nullish = null | undefined;
    type Fruit = 'apple' | 'pear' | 'orange';
    type Num = number | bigint;
  2. 元组:用 type,因为 interface 也做不到

    type row = [colOne: number, colTwo: string];
  3. 函数:用 type ,因为可读性

    // via type
    type Sum = (x: number, y: number) => number;
    
    // via interface
    interface Sum {
      (x: number, y: number): number;
    }
  4. UnionType:用 type ,因为 interface 没法 union

    type Fruit = 'apple' | 'pear' | 'orange';
    type Vegetable = 'broccoli' | 'carrot' | 'lettuce';
    
    // 'apple' | 'pear' | 'orange' | 'broccoli' | 'carrot' | 'lettuce';
    type HealthyFoods = Fruit | Vegetable;
  5. MappedType:用 type,因为 interface 做不到:

    type Fruit = 'apple' | 'orange' | 'banana';
    
    type FruitCount = {
      [key in Fruit]: number;
    }
    
    const fruits: FruitCount = {
      apple: 2,
      orange: 3,
      banana: 4
    };
  6. 除此以外,通常都用 interface,interface 有一个特殊性需要注意:

    interface Window {
      title: string
    }
    
    interface Window {
      ts: TypeScriptAPI
    }
    
    const src = 'const a = "Hello World"';
    window.ts.transpileModule(src, {});

那么,为什么我们说除此以外都用 interface 呢,因为官方是这么说的:

Performance · microsoft/TypeScript Wiki

Interfaces create a single flat object type that detects property conflicts, which are usually important to resolve! Intersections on the other hand just recursively merge properties, and in some cases produce never. Interfaces also display consistently better, whereas type aliases to intersections can't be displayed in part of other intersections. Type relationships between interfaces are also cached, as opposed to intersection types as a whole. A final noteworthy difference is that when checking against a target intersection type, every constituent is checked before checking against the "effective"/"flattened" type.

出于对检查性能的考虑,也建议你使用 interface

当然,如果要跳类型体操,那毫无疑问用的也是 type

至此,所有 typeinterface 的场景看上去理得泾渭分明,那为什么还会有这篇文章呢,因为今天遇到了一个 case:

// it works!
enum A {
  a,
  b
}

type V = {
  a: A
}

type CustomData<T> = T extends {
    [key: string]: string | number
} ? T : never

const a: CustomData<V> = {
  a: A.a
}
const enum A {
  a,
  b
}

interface K {
  a: A
}

type V = {
  a: A
}

type CustomData<T> = T extends {
    [key: string]: string | number
} ? T : never

const a: CustomData<K> = {
  // Type 'A' is not assignable to type 'never'.(2322)
  a: A.a
}

主要原因也是出于「性能」和「静态分析」时的考虑,上文中我们讲到了 interface 可以多次定义、扩充和覆盖;而 type 是一锤定音的,那么对于 type 来说,我们可以直接一口气推导到最终结果,而 interface 却不能。导致了这一差异性:

Index signature is missing in type (only on interfaces, not on type alias) · Issue #15300 · microsoft/TypeScript

(简单的来说还是实现成本略高。

植入部分

如果您觉得文章不错,可以通过赞助支持我。

如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。

标签: 知识, typescript

仅有一条评论

  1. pan

    干货满满

添加新评论