type 和 interface 别傻傻分不清了

512 阅读3分钟

介绍

type

类型别名,可以给一个类型起一个新名称。它不会新建一个类型,只是创建了新名称来引用那个类型。

type name = string;
type txt = 'hello' | 'world';

function sayIt(name: name) {
  // 错误示例1
  // const word: txt = 'hell';
  // 会报错提示:
  // const word: txt
  // 不能将类型“"hell"”分配给类型“txt”
  const word: txt = 'hello';
  console.log(word);
}
// 错误示例2
// sayIt(234);
// 会报错提示:
// 类型“number”的参数不能赋给类型“string”的参数。
sayIt('hali');

interface

描述和检查对象的数据结构以及值类型。

interface Example {
  txt: string;
  count: number
}

function example(props: Example) {
  console.log(props);
}
// 错误示例:
// example({ txt: 12, count: 100 });
// 会报错提示: 
// (property) Example.count: number
// 不能将类型“number”分配给类型“string”。
example({txt: 'hello', count: 100});

相同点

不管是 type 还是 interface 都可以用来约束值类型。

区别

  1. interface 会新建一个名称,可以在任何其他地方使用,类型错误提示中也会显示对应的名称(请看 介绍章节的 interface 代码中的错误示例 )。而type 不会新创建名字(因为只是给类型起了一个别名而已),所以在值类型错误的时候,错误提示并不会显示对应的名称,而是直接显示了对应的类型的字面量(请看 介绍章节的 type 代码中的错误示例 )。

  2. interface 会自动合并相同名称的的值类型约束内容,但相同名称的 type 会报错

     interface Example {
       txt: string
     }
    
     interface Example {
       count: number
     }
    
     const example: Example = {
       txt: '123',
       count: 123
     }
    
     // 报错:标识符“name”重复。
     type name = string;
     type name = number;
    
  3. interface 可以使用 extendsimplments ,而 type 不可以。

    类与接口

    实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现。这个特性大大提高了面向对象的灵活性。

    // https://ts.xcatliu.com/advanced/class-and-interfaces.html
    // 举例来说,门是一个类,防盗门是门的子类。如果防盗门有一个报警器的功能,我们可以简单的给防盗门添加一个报警方法。这时候如果有另一个类,车,也有报警器的功能,就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现它:
    interface Alarm {
        alert(): void;
    }
    
    interface Light {
        lightOn(): void;
        lightOff(): void;
    }
    
    class Car implements Alarm, Light {
        alert() {
            console.log('Car alert');
        }
        lightOn() {
            console.log('Car light on');
        }
        lightOff() {
            console.log('Car light off');
        }
    }
    
  4. type 可以实现值类型约束的并集和交集,而 interface 不可以。

    type ExampleA = {
      name: string
    };
    
    type ExampleB = {
      age: number
    };
    
    type ExampleC = ExampleA & ExampleB;
    type ExampleD = ExampleA | ExampleB;
    
    // 缺一不可
    const exampleC: ExampleC = {
      name: 'hello',
      age: 123
    }
    // 全缺不可
    const exampleD: ExampleD = {
      name: 'world',
      age: 123
    }
    
    // interface 也可以作为交集和并集子项
    interface ExampleE {
      name: string
    }
    
    interface ExampleF {
      age: number
    }
    
    type ExampleG = ExampleE & ExampleF;
    type ExampleH = ExampleE | ExampleF;
    
    // 缺一不可
    const exampleG: ExampleG = {
      name: 'hello',
      age: 456
    }
    // 全缺不可
    const exampleH: ExampleH = {
      name: 'hello',
      age: 456
    }
    
  5. type 可以声明元组(Tuples),而 interface 不可以。

    type Example = [string, number];
    
    // 但 interface 中可以使用元组
    interface ExampleA = {
      value: [string, number]
    }
    

总结

  1. 要始终记得 type 是类型别名,interface 是接口,名字突出意义;
  2. 一般情况下,两者都可以用来约束值类型;
  3. 特殊情况,如这个值约束的集合可能会被多处使用(如继承,实现)或可能有同名的但约束的值不同或值类型不同的话,就只能选择 interface 了;
  4. 特殊情况下,如果是声明元组或实现一个灵活的值类型集合的交叉或合并的时候,type 就可以大展身手了;
  5. 两者混合食用,味道更佳。

参考

Types vs. interfaces in TypeScript

高级类型

类与接口