草庐IT

【TypeScript】TS中type和interface在类型声明时的区别

不叫猫先生 2023-12-14 原文

🐱 个人主页:不叫猫先生
🙋‍♂️ 作者简介:2022年度博客之星前端领域TOP 2,前端领域优质作者、阿里云专家博主,专注于前端各领域技术,共同学习共同进步,一起加油呀!
💫优质专栏:vue3从入门到精通TypeScript从入门到实践
📢 资料领取:前端进阶资料可以找我免费领取
🔥 摸鱼学习交流:我们的宗旨是在工作中摸鱼,摸鱼中进步,期待大佬一起来摸鱼(文末有我wx或者私信)。


目录

一、专栏介绍

TypeScript从入门到实践专栏是博主在学习和工作过程中的总结,实用性非常强,内容会不断进行精进,欢迎订阅哦,学会TS不迷路。

TS系列标题
基础篇TS入门(一)
基础篇TS类型声明(二)
基础篇TS接口类型(三)
基础篇TS交叉类型&联合类型(四)
基础篇TS类型断言(五)
基础篇TS类型守卫(六)
进阶篇TS函数重载(七)
进阶篇TS泛型(八)
进阶篇TS装饰器(九)
进阶篇TS条件类型(十)
进阶篇TS自定义类型之对象属性必选、对象属性可选
进阶篇TS中type和interface在类型声明时的区别

二、TS中type和interface在类型声明时的区别

在TS中interfacetype都可以用来自定义数据类型,两者有许多相同之处,但是也有差别。我们一般选择 type 来定义基本类型别名、联合类型、元组等类型,而选择 interface 来定义复杂的对象、类、以及进行接口的继承。

1. 声明常见类型

(1)定义基本类型

type Age = number;

interface Person {
  name: string;
  age: Age;
}

(2)定义函数类型

type Greeting = (name: string) => string;

interface Greeter {
  (name: string): string;
}

(3)定义对象类型

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

interface Rectangle {
  width: number;
  height: number;
  position: Point;
}

(4)定义泛型

type List<T> = {
  data: T[];
  add: (item: T) => void;
}

interface List<T> {
  data: T[];
  add: (item: T) => void;
}

interface使用泛型的案例如下:

interface Container<T> {
  value: T;
  get(): T;
  set(value: T): void;
}

class NumberContainer implements Container<number> {
  value: number;
  get() {
    return this.value;
  }
  set(value: number) {
    this.value = value;
  }
}

const container = new NumberContainer();
container.set(42);
console.log(container.get()); // 

2. interface 可以被类(class)实现(implement),而 type 不能

interface Animal {
  name: string;
  speak: () => void;
}

class Dog implements Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  speak() {
    console.log("hello!");
  }
}
const myDog = new Dog("Sparky");
myDog.speak(); // 输出 hello

上面代码使用interface 定义了 namespeak 方法的 Animal 接口,然后使用 class 实现了该接口,并创建了一个 Dog 的实例,调用了speak方法。

type Animal = {
  name: string;
  speak: () => void;
}

class Dog implements Animal {
  // 这里会报错,因为 Animal 是一个类型别名,不能被 class 实现
}

上述代码会在编译时报错,因为 type 定义的 Animal 类型只是一个别名,并不是一个接口,不能被类实现。所以在需要定义一个可以被类实现的类型时,应该使用 interface 进行定义。

3. interface 支持 extends 实现接口的继承,而 type 不支持

(1)单接口继承

interface Animal {
	name: string;
	speak: () => void;
  }
  
interface Pet extends Animal {
	owner: string;
	play: () => void;
  }
  
class Dog implements Pet {
	name: string;
	owner: string;
	constructor(name: string, owner: string) {
	  this.name = name;
	  this.owner = owner;
	}
	speak() {
	  console.log("speak:hello");
	}
	play() {
	  console.log(`${this.name} is playing with ${this.owner}`);
	}
  }
  
  const myDog = new Dog("myPet", "Mr.Cat");
  myDog.speak(); // 输出 "speak:hello" 
  myDog.play(); // 输出 "myPet is playing with Mr.Cat" 

(2)多接口继承

由于 type 只是类型别名,不能包含具体的属性和方法实现,因此它不支持通过 extends 关键字实现接口的继承。如果需要继承类型别名,需要使用交叉类型进行组合。

当你需要让一个接口继承多个其他接口时,使用 interface 更加方便。因为 interface 允许你使用逗号分隔的方式来继承多个接口,而 type 只能使用交叉类型(&)来实现继承。

例如:

interface Person {
  name: string;
  age: number;
}

interface Employee {
  company: string;
  jobTitle: string;
}

interface Manager extends Person, Employee {
  teamSize: number;
}

const manager: Manager = {
  name: 'John Doe',
  age: 40,
  company: 'ABC Inc.',
  jobTitle: 'Manager',
  teamSize: 10,
};

在上面的例子中,我们定义了三个接口:PersonEmployee ManagerManager 接口继承了 PersonEmployee 接口,以及自己的属性 teamSize。如果使用 type 来定义 Manager 类型,那么就需要使用交叉类型来实现继承,但是这么实现起来就比较复杂。

总的来说,interface 和 type 都有自己的优势和使用场景。在 TypeScript 3.7 版本之后,type 也可以实现声明合并和继承多个类型的功能,因此在选择使用 interface 还是 type 时,应该根据具体情况来决定。

4.interface 可以定义多个同名接口并合并,而 type 不支持

当合并两个或多个具有相同名称的接口或类型时, interface 允许声明多个同名接口并将它们合并成一个。 例如:

interface User {
  name: string;
  age: number;
}

interface User {
  gender: 'male' | 'female';
}

const user: User = {
  name: '猫先生',
  age: 25,
  gender: 'male',
};

在上面的例子中,我们声明了两个同名的接口 User,并将它们合并成一个。如果使用 type 来定义 User 类型,那么就无法实现声明合并的功能,代码会直接报错。

5.type可以使用 typeof 获取实例的类型,而 interface 不支持

type Person = {
  name: string;
  age: number;
}

const john: Person = {
  name: "John",
  age: 30,
}

type PersonType = typeof john; // 类型为 { name: string, age: number }

在上图可以看到PersonType类型和Person类型一样,通过typeof获取john的数据类型,然后赋值给PersonType类型。

interface 不支持使用typeof操作符获取实例的类型。因为interface只是一种接口定义,它本身不是一个值,无法获取其类型。

有关【TypeScript】TS中type和interface在类型声明时的区别的更多相关文章

  1. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  2. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  3. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  4. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  5. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  6. ruby-on-rails - `a ||= b` 和 `a = b if a.nil 之间的区别? - 2

    我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行

  7. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  8. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  9. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

  10. ruby-on-rails - Rails 单表继承 : How to override the value written to the type field - 2

    在我的系统中,我已经定义了STI。Dog继承自Animal,在animals表中有一个type列,其值为"Dog"。现在我想让SpecialDog继承自dog,只是为了在某些特殊情况下稍微修改一下行为。数据还是一样。我需要通过SpecialDog运行的所有查询,以返回数据库中类型为Dog的值。我的问题是因为我有一个type列,rails将WHERE"animals"."type"IN('SpecialDog')附加到我的查询中,所以我不能获取原始的Dog条目。所以我想要的是以某种方式覆盖rails在通过SpecialDog访问数据库时使用的值,使其表现得像Dog。有没有办法覆盖用于类型

随机推荐