TypeScript type和interface的使用及区别

type

说明

type又叫类型别名(type alias),作用是给一个类型起一个新名字,不仅支持interface定义的对象结构,还支持基本类型、联合类型、交叉类型、元组等任何你需要手写的类型如下代码所示:

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: "1",
    name: "张三",
    age: 10,
    gender: "女"
};
const numbers: arr = [1, 2 , 3];

接口 interface

说明

接口是命名数据结构(例如对象)的另一种方式;与type 不同,interface仅限于描述对象类型。
接口的声明语法也不同于类型别名的声明语法。让我们将上面的类型别名 Person 重写为接口声明:

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

interface 和 type 的区别

  1. 接口可以扩展,但 type 不能 extends 和 implement 。

接口可以扩展,但type不能extends和implement。不过,type可以通过交叉类型实现interface的extends行为。interface 可以extends type,同时type也可以与interface类型交叉。

接口扩展

interface Name {
    name: string;
}
interface User extends Name {
    age: number;
}
let student:User={name:'wang',age:10}

type 交叉

type Name = {
    name: string
}

type User = Name & { age: number  };

let student:User={name:'张三',age:20}

interface 扩展 type

type Name = {
    name: string;
}

interface User extends Name {
    age: number;
}

let student:User={name:'张三',age:20}

type 与 interface 交叉

interface Name {
    name: string;
}

type User = Name & {
    age: number;
}
let student:User={name:'张三',age:20}
  1. 声明合并

type包含了interface大部分的能力,但声明合并这个能力只有interface支持。

我们可以声明同名的interface,它们会自动合并。但type不可以声明同名的类型,就像ES6里我们不能用let或const重复声明变量。如下代码所示:

interface Dog {
  name: 'dog';
}

interface Dog {
  run: () => void;
}

const dog: Dog = {
  name: 'dog',
  run: () => {
    console.log('dog run');
  }
};

// Duplicate identifier 'Fish'.
type Fish = {
  name: 'fish';
}

// Duplicate identifier 'Fish'.
type Fish = {
  swim: () => void;
}
  1. 冲突处理

interface生成一个打平的对象类型来检测属性的冲突,如果有冲突则报错。而type只是递归地合并属性,并不会在声明的时候报错,指定值的类型时则可能会报错,也可能不会报错。

interface DateObj {
  value: string;
}

// 报错
// Interface 'DateObj1' incorrectly extends interface 'DateObj'.
//   Types of property 'value' are incompatible.
//     Type 'Date' is not assignable to type 'string'.
interface DateObj1 extends DateObj {
  value: Date;
}

type DateObj2 = {
  value: string;
} & {
  value: Date;
}

// 声明值的类型时报错
// Type 'string' is not assignable to type 'string & Date'.
//   Type 'string' is not assignable to type 'Date'.
let foo: DateObj2 = { value: '' };

type Person = {
  getPermission: (id: string) => string;
};

type Staff = Person & {
  getPermission: (id: string[]) => string[];
};

// 不会报错
const AdminStaff: Staff = {
  getPermission: (id: string | string[]) =>{
    return (typeof id === 'string'?  'admin' : ['admin']) as string[] & string;
  }
}

type声明对象的交叉类型有时会产出never,有的时候不会,比如前面的DateObj2就没有产出never。

对象类型的交叉类型T1 & T2 & … & Tn要产出never,需要满足以下几个条件:

  • 两个或多个Tx类型有相同名字的属性;
  • 至少有一组同名的属性,某个类型比如T1的这个属性是字面量类型且所有Tx类型的这个属性都不是never;
  • 至少一个属性交叉之后的结果是never。
  1. index签名

interface默认是没有index签名的,而type有。
什么是index签名?对于对象或者数组,我们可以通过index签名描述所有value的类型,形式如下:

interface IStringArray {
  [index: number]: string;
}

interface IStringObject {
  [index: string]: string;
}

type TStringArray = {
  [index: number]: string;
}

type TStringObject = {
  [index: string]: string;
}

index可以换成任意字面量如K、P等。
然后,看下面的例子:

interface Animal {
  name: string;
}

function log(obj: Record<string, unknown>) {
  console.log(obj);
}

const dog: Animal = { name: 'dog' };

// 报错
// Argument of type 'Animal' is not assignable to parameter of type 'Record<string, unknown>'.
//   Index signature for type 'string' is missing in type 'Animal'.
log(dog);

一种解决办法是给interface Animal增加index签名:

interface Animal {
  [index: string]: unknown;
  name: string;
}

另一种办法是改成type写法:

type Animal = {
  name: string;
}
  1. 是否支持映射类型

nterface是不支持映射类型写法的,而type支持。以下写法都是不允许的:


interface X {
  [P in 'A' | 'B']: string;
}

interface StringToBoolean<T extends Record<string, string>> {
  [P in keyof T]: boolean;
}

改成type就没问题:


type X = {
  [P in 'A' | 'B']: string;
}

type StringToBoolean<T extends Record<string, string>> = {
  [P in keyof T]: boolean;
}

总结:

使用interface的场景:

  1. 当需要定义对象的结构和方法签名时,‌interface是首选。‌它主要用于描述对象应该具有哪些属性和方法,‌但不限制具体的实现。‌例如,‌在定义React组件的props时,‌使用interface可以确保组件的使用者不能随意添加未声明的属性,‌从而保持组件的稳定性和可预测性;
  2. 在编写三方库时,‌interface也是推荐的选择,‌因为它提供了更灵活的类型合并,‌能够应对未知的复杂使用场景。‌
  3. interface还支持继承和实现,‌可以通过继承已有的接口或类来扩展新的接口定义,‌这种特性在需要构建复杂的类型关系时非常有用。

使用type的场景:

  1. type几乎涵盖interface的所有能力,不能使用interface声明的类型都使用type,比如基础类型、联合类型、元组等;
  2. 当需要定义更复杂的类型,‌如联合类型、‌交叉类型等,‌type是一个更好的选择。‌它提供了更高级的类型操作能力,‌适用于需要组装或创建类型别名的场景;
  3. 在某些情况下,‌如果代码已经统一选择使用type来定义类型,‌那么为了保持代码的一致性,‌可以继续使用type来定义类型;
  4. type不支持类的实现和继承,‌但它的可合并性较弱,‌不支持自动合并同名接口,‌这在某些情况下可能是一个限制;

最近搞了一个前端知识分享的群,大家有什么问题都可以在里面交流,里面会不定期更新,欢迎大家的加入
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值