类型接口和类型别名

相同点

都可以声明对象和函数类型

  1. interface Point {
  2. x: number;
  3. y: number;
  4. }
  5. interface SetPoint {
  6. (x: number, y: number): void;
  7. }
  1. type Point = {
  2. x: number;
  3. y: number;
  4. };
  5. type SetPoint = (x: number, y: number) => void;

支持扩展

  1. interface Animal {
  2. name: string
  3. }
  4. interface Bear extends Animal {
  5. honey: boolean
  6. }
  7. const bear: Bear = {
  8. name: "Winnie",
  9. honey: true,
  10. }
  1. type Animal = {
  2. name: string
  3. }
  4. type Bear = Animal & {
  5. honey: boolean
  6. }
  7. const bear: Bear = {
  8. name: "Winnie",
  9. honey: true,
  10. }
  1. type Animal = {
  2. name: string
  3. }
  4. interface Bear extends Animal {
  5. honey: boolean
  6. }
  1. interface Animal {
  2. name: string
  3. }
  4. type Bear = Animal & {
  5. honey: boolean
  6. }

不同点

type 可以声明基本类型、联合类型或元组类型,而接口不行

  1. type MyNumber = number;
  2. type StringOrNumber = string | number;
  3. type Point = [number, number];

interface 可以自动合并类型,type 不支持

  1. interface Animal {
  2. name: string
  3. }
  4. interface Animal {
  5. tail: boolean
  6. }
  7. const dog: Animal = {
  8. name: "Tom",
  9. tail: true,
  10. }
  1. type Animal = {
  2. name: string
  3. }
  4. type Animal = {
  5. tail: boolean
  6. }
  7. // ERROR: Duplicate identifier 'Animal'.

利用同名接口自动合并的特性,在开发第三方库的时候,我们就可以为使用者提供更好的安全保障。比如webext-bridge这个库,使用interface定义了ProtocolMap接口,从而让使用者可自由地扩展ProtocolMap接口。

  1. import { ProtocolWithReturn } from 'webext-bridge'
  2. declare module 'webext-bridge' {
  3. export interface ProtocolMap {
  4. foo: { title: string }
  5. bar: ProtocolWithReturn<CustomDataType, CustomReturnType>
  6. }
  7. }

总结

官方推荐用interface,其他无法满足需求的情况下用type

但其实,因为联合类型和元组类型是很常用的,所以避免不了大量使用type的场景,一些复杂类型也需要通过组装后形成类型别名来使用。

所以,如果想保持代码统一,还是可选择使用type。通过上面的对比,类型别名其实可涵盖interface的大部分场景。

对于React组件中propsstate使用**type**这样能够保证使用组件的地方不能随意在上面添加属性。如果有自定义需求,可通过HOC二次封装。

编写三方库时使用**interface**,其更加灵活自动的类型合并可应对未知的复杂使用场景。