在接触 ts 相关代码的过程中,总能看到 interface 和 type 的身影。只记得,曾经遇到 type 时不懂查阅过,记得他们很像,相同的功能用哪一个都可以实现。但最近总看到他们,就想深入的了解一下他们。

interface:接口

TypeScript 的核心原则之一是对值所具有的结构进行类型检查。 而接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

  1. interface LabelledValue {
  2. label: string;
  3. }
  4. function printLabel(labelledObj: LabelledValue) {
  5. console.log(labelledObj.label);
  6. }
  7. let myObj = {size: 10, label: "Size 10 Object"};
  8. printLabel(myObj);

接口就好比一个名字,用来描述上面例子里的要求。

接口具有的特性:

  • 可选属性
  1. interface SquareConfig {
  2. color?: string;
  3. }
  • 只读属性
  1. interface Point {
  2. readonly x: number;
  3. }
  • 多余属性检查,防止使用不属于接口的属性
  1. interface Preson {
  2. name: string;
  3. age?: number;
  4. }
  5. let p1:Person = {name: '小明'} // 正确
  6. let p2:Person = {name: '小明', age: 18, sex: '男'}; // 报错
  7. // 绕过:多余属性不报错
  8. // 方式1
  9. let p = {name: '小明', age: 18, sex: '男'};
  10. let p3 = p;
  11. // 方式2
  12. interface Preson {
  13. name: string;
  14. age?: number;
  15. [propName: string]: any
  16. }
  17. let p4 = {name: '小明', age: 18, sex: '男'};
  • 函数类型
  1. interface SearchFunc {
  2. (source: string, subString: string): boolean;
  3. }
  • 索引类型: 针对数组
  1. interface StringArray {
  2. [index: number]: string;
  3. }
  4. let myArray: StringArray;
  5. myArray = ["Bob", "Fred"];
  • 类类型

    • 类实现接口
    1. interface ClockInterface {
    2. currentTime: Date;
    3. setTime(d: Date);
    4. }
    5. class Clock implements ClockInterface {
    6. currentTime: Date;
    7. setTime(d: Date) {
    8. this.currentTime = d;
    9. }
    10. constructor(h: number, m: number) { }
    11. }
    • 接口继承接口,可多个
    1. interface Shape {
    2. color: string;
    3. }
    4. interface PenStroke {
    5. penWidth: number;
    6. }
    7. interface Square extends Shape, PenStroke {
    8. sideLength: number;
    9. }
    10. let square = <Square>{};
    11. square.color = "blue";
    12. square.sideLength = 10;
    13. square.penWidth = 5.0;

type:类型别名

type 会给一个类型起个新名字。 type 有时和 interface 很像,但是可以作用于原始值(基本类型),联合类型,元组以及其它任何你需要手写的类型。

举例:

  1. type Name = string; // 基本类型
  2. type NameResolver = () => string; // 函数
  3. type NameOrResolver = Name | NameResolver; // 联合类型
  4. function getName(n: NameOrResolver): Name {
  5. if (typeof n === 'string') {
  6. return n;
  7. } else {
  8. return n();
  9. }
  10. }

起别名不会新建一个类型 - 它创建了一个新 名字来引用那个类型。给基本类型起别名通常没什么用,尽管可以做为文档的一种形式使用。

同接口一样,类型别名也可以是泛型 - 我们可以添加类型参数并且在别名声明的右侧传入:

  1. type Container<T> = { value: T };

也可以使用类型别名来在属性里引用自己:

  1. type Tree<T> = {
  2. value: T;
  3. left: Tree<T>;
  4. right: Tree<T>;
  5. }

与交叉类型一起使用,我们可以创建出一些十分稀奇古怪的类型。

  1. type LinkedList<T> = T & { next: LinkedList<T> };
  2. interface Person {
  3. name: string;
  4. }
  5. var people: LinkedList<Person>;
  6. var s = people.name;
  7. var s = people.next.name;
  8. var s = people.next.next.name;
  9. var s = people.next.next.next.name;

然而,类型别名不能出现在声明右侧的任何地方。

  1. type Yikes = Array<Yikes>; // error

interface vs type

1. Objects / Functions

两者都可以用来描述对象或函数的类型,但是语法不同。

Interface

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

Type alias

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

2. Other Types

与接口不同,类型别名还可以用于其他类型,如基本类型(原始值)、联合类型、元组。

  1. // primitive
  2. type Name = string;
  3. // object
  4. type PartialPointX = { x: number; };
  5. type PartialPointY = { y: number; };
  6. // union
  7. type PartialPoint = PartialPointX | PartialPointY;
  8. // tuple
  9. type Data = [number, string];
  10. // dom
  11. let div = document.createElement('div');
  12. type B = typeof div;

3. Extend

两者都可以扩展,但是语法又有所不同。此外,请注意接口和类型别名不是互斥的。接口可以扩展类型别名,反之亦然。

Interface extends interface

  1. interface PartialPointX { x: number; }
  2. interface Point extends PartialPointX { y: number; }

Type alias extends type alias

  1. type PartialPointX = { x: number; };
  2. type Point = PartialPointX & { y: number; };

Interface extends type alias

  1. type PartialPointX = { x: number; };
  2. interface Point extends PartialPointX { y: number; }

Type alias extends interface

  1. interface PartialPointX { x: number; }
  2. type Point = PartialPointX & { y: number; };

4. class Implements

类可以以相同的方式实现接口或类型别名。但是请注意,类和接口被认为是静态的。因此,它们不能实现/扩展命名联合类型的类型别名。

  1. interface Point {
  2. x: number;
  3. y: number;
  4. }
  5. class SomePoint implements Point {
  6. x: 1;
  7. y: 2;
  8. }
  9. type Point2 = {
  10. x: number;
  11. y: number;
  12. };
  13. class SomePoint2 implements Point2 {
  14. x: 1;
  15. y: 2;
  16. }
  17. type PartialPoint = { x: number; } | { y: number; };
  18. // FIXME: can not implement a union type
  19. class SomePartialPoint implements PartialPoint {
  20. x: 1;
  21. y: 2;
  22. }

5. extends class

类定义会创建两个东西:类的实例类型和一个构造函数。 因为类可以创建出类型,所以你能够在允许使用接口的地方使用类。

  1. class Point {
  2. x: number;
  3. y: number;
  4. }
  5. interface Point3d extends Point {
  6. z: number;
  7. }

6. Declaration merging

与类型别名不同,接口可以定义多次,并将被视为单个接口(合并所有声明的成员)。

  1. // These two declarations become:
  2. // interface Point { x: number; y: number; }
  3. interface Point { x: number; }
  4. interface Point { y: number; }
  5. const point: Point = { x: 1, y: 2 };

7. 计算属性,生成映射类型

type 能使用 in 关键字生成映射类型,但 interface 不行。

语法与索引签名的语法类型,内部使用了 for .. in。 具有三个部分:

  • 类型变量 K,它会依次绑定到每个属性。
  • 字符串字面量联合的 Keys,它包含了要迭代的属性名的集合。
  • 属性的结果类型。
  1. type Keys = "firstname" | "surname"
  2. type DudeType = {
  3. [key in Keys]: string
  4. }
  5. const test: DudeType = {
  6. firstname: "Pawel",
  7. surname: "Grzybek"
  8. }
  9. // 报错
  10. //interface DudeType2 {
  11. // [key in keys]: string
  12. //}

7. 其他细节

  1. export default interface Config {
  2. name: string
  3. }
  4. // export default type Config1 = {
  5. // name: string
  6. // }
  7. // 会报错
  8. type Config2 = {
  9. name: string
  10. }
  11. export default Config2

总结

interface 和 type 很像,很多场景,两者都能使用。但也有细微的差别:

  • 类型:对象、函数两者都适用,但是 type 可以用于基础类型、联合类型、元祖。
  • 同名合并:interface 支持,type 不支持。
  • 计算属性:type 支持, interface 不支持。

总的来说,公共的用 interface 实现,不能用 interface 实现的再用 type 实现。主要是一个项目最好保持一致。

参考:

【区分】Typescript 中 interface 和 type的更多相关文章

  1. TypeScript中的private、protected

    首先我们要清楚 private . protected 现阶段只是javascript中的保留字(Reserved words),而非关键字(Keywords ).因此TypeScript中的纯类型声 ...

  2. 5种在TypeScript中使用的类型保护

    摘要:在本文中,回顾了TypeScript中几个最有用的类型保护,并通过几个例子来了解它们的实际应用. 本文分享自华为云社区<如何在TypeScript中使用类型保护>,作者:Ocean2 ...

  3. TypeScript 中的方法重载

    方法重载(overload)在传统的静态类型语言中是很常见的.JavaScript 作为动态语言, 是没有重载这一说的.一是它的参数没有类型的区分,二是对参数个数也没有检查.虽然语言层面无法自动进行重 ...

  4. 十分钟教你理解TypeScript中的泛型

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者.原文出处:https://blog.bitsrc.io/understanding-generics-in-t ...

  5. TypeScript中使用getElementXXX()

    如果只是看解决方法,可以直接跳到第二小节 简述 Angular 1.x版本是用JavaScript编写的,我们在百度Angular经常会搜索到AngularJS,并不是JavaScript的什么衍生版 ...

  6. 聊聊 TypeScript 中的类型保护

    聊聊 TypeScript 中的类型保护 在 TypeScript 中使用联合类型时,往往会碰到这种尴尬的情况: interface Bird { // 独有方法 fly(); // 共有方法 lay ...

  7. typescript 中的 infer 关键字的理解

    infer 这个关键字,整理记录一下,避免后面忘记了.有点难以理解呢. infer infer 是在 typescript 2.8中新增的关键字. infer 可以在 extends 条件类型的字句中 ...

  8. TypeScript 中命名空间与模块的理解?区别?

    一.模块 TypeScript 与ECMAScript 2015 一样,任何包含顶级 import 或者 export 的文件都被当成一个模块 相反地,如果一个文件不带有顶级的import或者expo ...

  9. typescript中对象属性可选属性与只读属性与索引签名

    可选属性 type类型别名 type 会给一个类型起个新名字. type 有时和 interface 很像,但是可以作用于原始值(基本类型),联合类型,元组以及其它任何你需要手写的类型. interf ...

随机推荐

  1. Redis 学习笔记(篇三):跳表

    跳表 跳表(skiplist)是一种有序的数据结构,是在有序链表的基础上发展起来的. 在 Redis 中跳表是有序集合(sort set)的底层实现之一. 说到 Redis 中的有序集合,是不是和 J ...

  2. Spring源码解读之BeanFactoryPostProcessor的处理

    前言 前段时间旁听了某课堂两节Spring源码解析课,刚好最近自己又在重新学习中,便在这里记录一下学习所得.我之前写过一篇博文,是介绍BeanFactoryPostProcessor跟BeanPost ...

  3. UI-grid 表格内容可编辑(enableCellEdit可指定列编辑)

    在网上搜索了很多关于UI-Grid的问题 很遗憾好少啊啊啊 不过有API还是比较欣慰的 官方API:UI Grid 还有一位大佬的翻译的中文API:angularjs ui-grid中文api 行编辑 ...

  4. 【过时】Maven简单安装与集成Eclipse

    前言:MyEclipse前期产品对maven支持性不是很好,而且对maven版本要求很严格,集成后有很多问题,所以推荐 使用比较新的IDE进行开发. 1.环境搭建(只是用IDE集成的不需要系统安装,请 ...

  5. TCP/IP 第四、五章

    1, 2, 整个arp请求的过程. 3,arp -a 获取arp高速缓存.一般arp高速缓存存活时间20分钟,不完整的表项设置为3分钟.因为机器的ip地址可能发生改变. 4, 5,arp一般是操作系统 ...

  6. Oracle Awr报告_生成

    AWR的概念 Oracle数据库是一个使用量很多的数据库,关于Oracle数据库的性能.Oracle10g以后,Oracle提供了一个性能检测的工具:AWR(Automatic Workload Re ...

  7. c# 开发ActiveX控件,添加事件,QT调用事件

    c# 开发 ActiveX 的过程参考我的另一篇文章 :  https://www.cnblogs.com/baqifanye/p/10414004.html 本篇讲如何 在C# 开发的ActiveX ...

  8. 使用CocoaPods创建自己的私有库-iOS组件化第一步

    目前iOS组件化常用的解决方案是Pod+路由+持续集成,通常架构设计完成后第一步就是将原来工程里的模块按照架构图分解为一个个独立的pod工程(组件),今天我们就来看看如何创建一个Pod私有库. 新建: ...

  9. JAVA 从一个List里删除包含另一个List的数据

    /** * 从listA里删除listB里有的数据 * @param listA * @param listB * @return */ public static List<String> ...

  10. 使用 Mybatis 框架 jdbc 方式批量写入 SQL Server,报错 com.microsoft.sqlserver.jdbc.SQLServerException 传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确。此 RPC 请求中提供了过多的参数,最多应为2100

    这个错是sqlserver抛出来的. 进过验证,上述错误中的2100为插入的总字段数. 比如下面这种插入方式,values后面的一个括号里的字段为30个,那么后面最多只能加70条,即这种批量插入方式一 ...