接口interface与类型别名type的区别

203 阅读2分钟

interface与type算是同一概念的两种句法,类似与函数表达式与函数声明之间的关系,不过两者之间存在一些细微的差别

正常情况

type CarType = {
  name: string;
  number: number;
  sale: boolean;
};

interface CarInterface {
  name: string;
  number: number;
  sale: boolean;
}

这两个声明都定义了一个结构,两者可以相互赋值,在这种情况下实质上两者完全一样

使用类型组合

// 类型组合
type AnimalType = {
    dog: string;
    cat: string;
};

type HasPig = AnimalType & {
    pig: string;
};

interface AnimalInterface {
    dog: string;
    cat: string;
}

interface HasPigInterface extends AnimalInterface {
    pig: string;
}

type 类型别名使用的是 '&'进行扩展, interface 使用的是extends 进行扩展,结果是完全一致的

区别

1、类型别名type更为通用,右边可以是任何类型,包括表达式,而接口声明interface中,右边必须是结构,下面例子类型别名type不能使用接口interface重写

type A = number;
type B = A | string;

这种定义在接口声明中是无法实现的

2、扩展接口时,typescript会检查扩展的接口是否可以赋值,如下:

interface AA {
    good(x: number): string;
    bad(x: number): string;
}

interface BB extends AA { // 报错: 属性“bad”的类型不兼容
    good(x: string | number): string;
    bad(x: string): string;
}

而使用type将不会有这种问题,如下:

type AAA = {
    good(x: number): string;
    bad(x: number): string;
}
type BBB = AAA & {
    good(x: string | number): string;
    bad(x: string): string;
}

typescript 将会将AAA尽力的拓展,最终结果是重载了bad的签名,而不会抛出错误,得到的结果如下:

type BBB = {
    good(x: string | number): string;
    bad(x: string | number): string; // typescript重载了bad函数
}

3、同一作用域中的多个同名接口将自动合并,而类型别名将会导致编译报错

interface CCC {
    a: string
}
interface CCC {
    b: string
}
// CCC => { a: string; b: string }

但这里要注意的是,两个接口声明不能有冲突,否则将会报错,如下a冲突了:

interface DDD {
  a: string
}

interface DDD {
  a: number // Error: 后续属性声明必须属于同一类型。属性“a”的类型必须为“string”,但此处却为类型“number”。ts(2717)
}

而 type 将直接报错,如下:

type EEE = {}
type EEE = {} // 报错: 标识符“EEE”重复。ts(2300)

总结

建议日常通常采用type,这样可以达到拓展性,无法实现需求再改用interface