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

interface:接口

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

interface LabelledValue {
label: string;
} function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
} let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

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

接口具有的特性:

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

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

type:类型别名

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

举例:

type Name = string; // 基本类型
type NameResolver = () => string; // 函数
type NameOrResolver = Name | NameResolver; // 联合类型 function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}

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

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

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

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

type Tree<T> = {
value: T;
left: Tree<T>;
right: Tree<T>;
}

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

type LinkedList<T> = T & { next: LinkedList<T> };

interface Person {
name: string;
} var people: LinkedList<Person>;
var s = people.name;
var s = people.next.name;
var s = people.next.next.name;
var s = people.next.next.next.name;

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

type Yikes = Array<Yikes>; // error

interface vs type

1. Objects / Functions

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

Interface

interface Point {
x: number;
y: number;
} interface SetPoint {
(x: number, y: number): void;
}

Type alias

type Point = {
x: number;
y: number;
}; type SetPoint = (x: number, y: number) => void;

2. Other Types

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

// primitive
type Name = string; // object
type PartialPointX = { x: number; };
type PartialPointY = { y: number; }; // union
type PartialPoint = PartialPointX | PartialPointY; // tuple
type Data = [number, string]; // dom
let div = document.createElement('div');
type B = typeof div;

3. Extend

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

Interface extends interface

interface PartialPointX { x: number; }
interface Point extends PartialPointX { y: number; }

Type alias extends type alias

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

Interface extends type alias

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

Type alias extends interface

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

4. class Implements

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

interface Point {
x: number;
y: number;
} class SomePoint implements Point {
x: 1;
y: 2;
} type Point2 = {
x: number;
y: number;
}; class SomePoint2 implements Point2 {
x: 1;
y: 2;
} type PartialPoint = { x: number; } | { y: number; }; // FIXME: can not implement a union type
class SomePartialPoint implements PartialPoint {
x: 1;
y: 2;
}

5. extends class

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

class Point {
x: number;
y: number;
} interface Point3d extends Point {
z: number;
}

6. Declaration merging

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

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

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

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

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

  • 类型变量 K,它会依次绑定到每个属性。
  • 字符串字面量联合的 Keys,它包含了要迭代的属性名的集合。
  • 属性的结果类型。
type Keys = "firstname" | "surname"

type DudeType = {
[key in Keys]: string
} const test: DudeType = {
firstname: "Pawel",
surname: "Grzybek"
} // 报错
//interface DudeType2 {
// [key in keys]: string
//}

7. 其他细节

export default interface Config {
name: string
} // export default type Config1 = {
// name: string
// }
// 会报错 type Config2 = {
name: string
}
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. Qt Quick 事件处理之信号与槽(foruok的博客)

    前面两篇文章<QML 语言基础>和<Qt Quick 简单教程>中我们介绍了 QML 语言的基本语法和 Qt Quick 的常见元素,亲们,通过这两篇文章,您应该已经可以完成简 ...

  2. 设置qt插件路径

    1.在Qt中使用 WebKit 浏览器核心 使用 QtWebKit 需要在工程文件(*.pro)中加入: QT +=webkitQT += network 2.QtWebKit的flash支持 QtW ...

  3. qt实现-给SQLITE添加自定义函数(对某个字段进行加密)

    需要使用sqlite里的password对某个字段进行加密,由于使用的sqlite是由QT封装好的QSqlDatabase,没有发现加载扩展函数的方法,所以自己实现了一个. 在网上也没找到相应的参考, ...

  4. C++与QML混合编程实现2048

    http://blog.csdn.net/ieearth/article/details/42705305

  5. spring之@value详解二(转载)

    1.1 前提 测试属性文件:advance_value_inject.properties server.name=server1,server2,server3 #spelDefault.value ...

  6. Spring事务原理一探

    概括来讲,事务是一个由有限操作集合组成的逻辑单元.事务操作包含两个目的,数据 一致以及操作隔离.数据一致是指事务提交时保证事务内的所有操作都成功完成,并且 更改永久生效:事务回滚时,保证能够恢复到事务 ...

  7. php回调函数设计

    <?php namespace Server; /** * 回调函数设计 * Class Server * @package Server */ class Server { public fu ...

  8. solr 重要的知识点

    1 solr 查询参数说明 常用 ) q - 查询字符串,必须的. ) fl - 指定返回那些字段内容,用逗号或空格分隔多个. ) start - 返回第一条记录在完整找到结果中的偏移位置, 开始,一 ...

  9. Frameset下的frame动态隐藏

    技术涉及:html+Jquery 不多说直接上图:由于是  .netcore  MVC Web应用对于大家来说不一致的话可供参考哦

  10. GIT \ SVN 版本管理 git + gitHub

    场景1   想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件"另存为--"一个新的Word文件,再接着改,改到一定程度,再"另存为--"一个新 ...