详解TypeScript中type与interface的区别

目录
  • 类型别名 type
  • 接口 interface
  • interface和type的相似之处
    • 都可以描述 Object和Function
    • Type
    • Interface
    • 二者都可以被继承
    • interface 继承 interface
    • interface 继承 type
    • type 继承 type
    • type 继承 interface
    • 实现 implements
  • 二者区别
    • 1. 定义基本类型别名
    • 2. 声明联合类型
    • 3. 声明元组
    • 4. 声明合并
    • 5. 索引签名问题
  • 总结

类型别名 type

首先认识一下什么是类型别名?

类型别名用来给一个类型起个新名字,使用 type 创建类型别名,类型别名不仅可以用来表示基本类型,还可以用来表示对象类型、联合类型、元组和交集。让我们看一些例子:

type userName = string; // 基本类型
type userId = string | number; // 联合类型
type arr = number[];
// 对象类型
type Person = {
    id: userId; // 可以使用定义类型
    name: userName;
    age: number;
    gender: string;
    isWebDev: boolean;
};
// 范型
type Tree<T> = { value: T };
const user: Person = {
    id: "901",
    name: "椿",
    age: 22,
    gender: "女",
    isWebDev: false,
};
const numbers: arr = [1, 8, 9];

接口 interface

接口是命名数据结构(例如对象)的另一种方式;与type 不同,interface仅限于描述对象类型。

接口的声明语法也不同于类型别名的声明语法。让我们将上面的类型别名 Person 重写为接口声明:

interface Person {
    id: userId;
    name: userName;
    age: number;
    gender: string;
    isWebDev: boolean;
}

interface和type的相似之处

在讨论二者区别之前, 首先看一下二者的相似之处(为何开发中,我们觉得用哪个都一样)

都可以描述 Object和Function

两者都可以用来描述对象或函数,但语法不同:

Type

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

Interface

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

二者都可以被继承

interface 和 type 都可以继承。

另一个值得注意的是,接口和类型别名并不互斥。类型别名可以继承接口,反之亦然。只是在实现形式上,稍微有些差别。

interface 继承 interface

interface Person{
    name:string
}
interface Student extends Person { stuNo: number }

interface 继承 type

type Person{
    name:string
}
interface Student extends Person { stuNo: number }

type 继承 type

type Person{
    name:string
}
type Student = Person & { stuNo: number }

type 继承 interface

interface Person{
    name:string
}
type Student = Person & { stuNo: number }

实现 implements

类可以实现interface 以及 type(除联合类型外)

interface ICat{
    setName(name:string): void;
}
class Cat implements ICat{
    setName(name:string):void{
        // todo
    }
}
// type
type ICat = {
    setName(name:string): void;
}
class Cat implements ICat{
    setName(name:string):void{
        // todo
    }
}

上面提到了特殊情况,类无法实现联合类型, 是什么意思呢?

type Person = { name: string; } | { setName(name:string): void };
// 无法对联合类型Person进行实现
// error: A class can only implement an object type or intersection of object types with statically known members.
class Student implements Person {
  name= "张三";
  setName(name:string):void{
        // todo
    }
}

上面聊了interface与 type的相似之处, 接下来就来看看他们的区别。

二者区别

1. 定义基本类型别名

type可以定义基本类型别名, 但是interface无法定义,如:

type userName = string
type stuNo = number
...

2. 声明联合类型

type可以声明联合类型, 例如:

type Student = {stuNo: number} | {classId: number}

3. 声明元组

type可以声明 元组类型:

type Data = [number, string];

以上都是 type能做到, 而interface做不到的, 接下来聊聊type做不到的

4. 声明合并

如果你多次声明一个同名的接口,TypeScript 会将它们合并到一个声明中,并将它们视为一个接口。这称为声明合并, 例如:

interface Person { name: string }
interface Person { age: number }
let user: Person = {
    name: "Tolu",
    age: 0,
};

这种情况下,如果是type的话,重复使用Person是会报错的:

type Person { name: string };
// Error: 标识符“Person”重复。ts(2300)
type Person { age: number }

5. 索引签名问题

如果你经常使用TypeScript, 一定遇到过相似的错误:

Type 'xxx' is not assignable to type 'yyy'

Index signature is missing in type 'xxx'.

看个例子来理解问题:

interface propType{
    [key: string] : string
}
let props: propType
type dataType = {
    title: string
}
interface dataType1 {
    title: string
}
const data: dataType = {title: "订单页面"}
const data1: dataType1 = {title: "订单页面"}
props = data
// Error:类型“dataType1”不可分配给类型“propType”; 类型“dataType1”中缺少索引签名
props = data1

我们发现dataType和dataType1对应的类型一样,但是interface定义的就赋值失败,是什么原因呢?刚开始百思不解,最后我在 stack overflow上找到了一个相似的问题:

并且很幸运的找到了有效的答案:

翻译过来的大致意思就是:

Record<string,string>与{[key:string]:string}相同。只有当该类型的所有属性都已知并且可以对照该索引签名进行检查时,才允许将子集分配给该索引签名类型。在您的例子中,从exampleType到Record<string,string>的所有内容都是可分配的。这只能针对对象字面量类型进行检查,因为一旦声明了对象字面量类型,就无法更改它们。因此,索引签名是已知的。

相反,在你使用interface去声明变量时,它们在那一刻类型并不是最终的类型。由于interfac可以进行声明合并,所以总有可能将新成员添加到同一个interface定义的类型上。

再结合:point_up_2:第4点 声明合并的讲解, 这样就很好理解了。就是说interface定义的类型是不确定的, 后面再来一个:

interface propType{
    title:number
}

这样propType类型就被改变了。

总结

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

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

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

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

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

以上就是详解TypeScript中type与interface的区别的详细内容,更多关于TypeScript type interface区别的资料请关注我们其它相关文章!

(0)

相关推荐

  • TypeScript与JavaScript项目里引入MD5校验和

    目录 一.什么是MD5校验和? 二.MD5校验和的优点和漏洞 三.如何在TS项目里引入MD5校验和? 四.MD5校验的使用 五.另一个npm依赖包的使用方法 一.什么是MD5校验和? MD5,是Message Digest Algorithm 5的缩写,即消息摘要算法版本5. 消息摘要算法通过对所有数据提取指纹信息以实现数据签名.数据完整性校验等功能,由于其不可逆性,有时候会被用做敏感信息的加密.消息摘要算法也被称为哈希(Hash)算法或散列算法.任何消息经过散列函数处理后,都会获得唯一的散列值

  • typeScript 核心基础之接口interface

    目录 1.接口定义 2.接口继承 3.类实现接口 前言: 在面向对象语言中,接口是一个很重要的概念,它是对行为的抽象.接口也叫 interface . 在 js 中没有接口这个概念,它是新增的.该如何定义呢?下面来一起学习吧 1.接口定义 接口的作用: 在面向对象编程中,接口是一种规范的定义,它定义了行为和动作规范: 在程序设计内,接口起到一种限制和规范的作用: 接口一般使用 interface 关键字来定义,名字首字母需要大写.在项目中定义接口的时候,一般在名字前加一个大写 I 字母,能够快速

  • 在Typescript中如何使用for...in详解

    如何在Typescript中使用for...in ?本人在TS中用for...in出现了些问题,也想到了一些解决方法.那么先来看看下面报错的代码吧. interface ABC { a: string b: string } const x: ABC = { a:'1', b:'2' } const y: ABC = { a:'3', b:'4' } for (const key in x) { // 在类型 "ABC" 上找不到具有类型为 "string" 的参数

  • TypeScript中函数重载写法

    目录 1. 函数签名 2.函数重载 2.1 重载签名是可调用的 2.1 实现签名必须是通用的 3.方法重载 4. 何时使用函数重载 5.总结 前言: 大多数函数接受一组固定的参数.但有些函数可以接受可变数量的参数,不同类型的参数,甚至可以根据你调用函数的方式返回不同的类型.为了注释这样的函数,TypeScript 提供了函数重载功能. 1. 函数签名 我们先来考虑一个函数,它返回给一个特定的人的问候信息. function greet(person: string): string { retu

  • TypeScript 使用 Tuple Union 声明函数重载

    问题: TypeScript 中为函数添加多个签名后,依然需要添加相应的代码来判断并从不同的签名参数列表中获取对应的参数.过去常见的写法: function refEventEmitter(event?: string): void; function refEventEmitter(event: string, callback: () => void): void; function refEventEmitter(callback: () => void): void; function

  • 详解TypeScript中type与interface的区别

    目录 类型别名 type 接口 interface interface和type的相似之处 都可以描述 Object和Function Type Interface 二者都可以被继承 interface 继承 interface interface 继承 type type 继承 type type 继承 interface 实现 implements 二者区别 1. 定义基本类型别名 2. 声明联合类型 3. 声明元组 4. 声明合并 5. 索引签名问题 总结 类型别名 type 首先认识一下

  • typescript中type和interface的区别有哪些

    目录 前言 type和interface的相同点 type和interface的不同点 结语 如何选择 Interface . Type 总结 前言 在typescript里面,有两个概念十分容易混淆,那便是 type 和 interface,它俩都可以用来表示 接口,但是实际使用上会存在一些差异,因此本篇文章就准备聊聊它俩,彻底弄清它俩的联系与区别,废话不多说,开搞! type和interface的相同点 在我看来,它俩就是对 接口定义 的两种不同形式,目的都是一样的,都是用来定义 对象 或者

  • TypeScript中type和interface的区别及注意事项

    目录 前言 概念 type interface 异同点 不同点 相同点 补充:Ts中type和interface定义类型扩展类型的方法 总结 前言 在 TS 中,type 和 interface相似,都可以给类型命名并通过该名字来引用表示的类型.不过它们之间是存在一些差别的,我们在使用时也需要注意一些特殊场景. 概念 type type关键字是声明类型别名的关键字.它的语法如下: type AliasName = Type; type:声明类型别名的关键字 AliasName:类型别名的名称 T

  • 详解TypeScript中的类型保护

    概述 在 TypeScript 中使用联合类型时,往往会碰到这种尴尬的情况: interface Bird { // 独有方法 fly(); // 共有方法 layEggs(); } interface Fish { // 独有方法 swim(); // 共有方法 layEggs(); } function getSmallPet(): Fish | Bird { // ... } let pet = getSmallPet(); pet.layEggs(); // 正常 pet.swim();

  • 详解Java中Callable和Future的区别

    目录 Java中为什么需要Callable Callable和Runnable的区别 Future和RunnableFuture 不使用Callable和Future,仅使用Runnable实现相同功能 Java中为什么需要Callable 在java中有两种创建线程的方法: 一种是继承Thread类,重写run方法: public class TestMain { public static void main(String[] args) { MyThread t1 = new MyThre

  • 详解JAVA中implement和extends的区别

    详解JAVA中implement和extends的区别 extends是继承父类,只要那个类不是声明为final或者那个类定义为abstract的就能继承,Java中不支持多重继承,但是可以用接口来实现,这样就要用到implements,继承只能继承一个类,但implements可以实现多个接口,用逗号分开就行了比如class A extends B implements C,D,E implements是一个类实现一个接口用的关键字,他是用来实现接口中定义的抽象方法. 还有几点需要注意: (1

  • 详解java中接口与抽象类的区别

    详解java中接口与抽象类的区别 1.abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系.但是,一个类却可以实现多个interface. 2.在abstract class 中可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在 interface中一般不定义数据成员),所有的成员方法都是abstract的. 3.abstract c

  • 详解C++中typedef 和 #define 的区别

    1.执行上不同 关键字 typedef 在编译阶段有效,由于是在编译阶段,因此 typedef 有类型检查的功能. #define 则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的字符串替换,而不进行任何检查. 例如:typedef 会做相应的类型检查 typedef unsigned int UINT; void func() { UINT value = "abc"; // error C2440: 'initializing' : cannot convert

  • 详解Python中range()与xrange()的区别

    目录 前言 返回类型 记忆 操作使用 Speed 前言 range() 和 xrange() 是两个函数,可用于在 Python的 for 循环中迭代一定次数.在 Python 3 中,没有 xrange,但 range 函数的行为类似于 Python 2 中的 xrange.如果要编写可在 Python 2 和 Python 3 上运行的代码,则应使用 range(). range()  – 这将返回一个范围对象(一种可迭代的类型). xrange()  – 此函数返回生成器对象,该生成器对象

  • 一文详解webpack中loader与plugin的区别

    目录 一.Loader 1.loader的作用: 2.loader的工作原理: 3. Loader 执行顺序 4.如何开发一个loader 二.Plugin 1.plugin解决其他的更多的自动化打包工作 2.自定义插件 常见的Loader和Plugin loader: plugin: 一.Loader 1.loader的作用: webpack 只能直接处理 javascript 格式的代码.任何非 js 文件都必须被预先处理转换为 js 代码,才可以参与打包.loader(加载器)就是这样一个

随机推荐