type
和 class
有什么区别?
type Point = {
x: number, y: number
}
let p = new Point();
以上结果:
“Point”仅指类型,但在这里被用作值。
为什么会这样?我当然不是不使用
Point
作为值,而是使用它来实例化类型。
在什么情况下我需要使用
type
,因为class
不适合?
Typescript 有两个不同的宇宙,它们在某些方面有所接触:值空间和类型空间。类型空间是定义类型的地方,类型会被完全删除并且在运行时不存在。值空间包含值并且显然会在运行时存在。
什么是价值?值文字、变量、常量和参数显然都是值。函数和类声明也是值,因为它们确实有一个运行时对象支持它们,即函数对象和类构造函数(也是一个函数)。 枚举也是值,因为它们在运行时由对象备份。
什么是类型?任何带有
type
关键字的定义都是类型以及接口、类声明 和 枚举
您会注意到我在两个空间中都提到了类声明。类同时存在于类型空间和值空间中。这就是为什么我们可以在类型注释(
let foo: ClassName
)和表达式(例如new ClassName()
)中使用它们。
枚举也跨越两个世界,它们也代表我们可以在注释中使用的类型,而且还代表将保存枚举的运行时对象。
类型空间和值空间中的名称不会冲突,这就是为什么我们可以定义同名的类型和变量:
type Foo = { type: true }
var Foo = { value : true } // No error, no relation to Foo just have the same name in value space
类声明和枚举,因为它们跨越两个空间,将“用完”两个空间中的名称,因此我们无法定义与类声明或枚举同名的变量或类型(尽管我们可以进行合并,但是这是不同的概念)
在您的具体情况下,
Point
只是一种类型,我们可以在类型注释中使用它,而不是我们可以在需要运行时存在的表达式中使用的东西。在这种情况下,该类型很有用,因为它允许编译器从结构上检查对象文字是否可分配给 Point
类型:
let p: Point = { x: 10, y: 15 }; // OK
let p: Point = { x: 10, y: 15, z: 10 }; // Error
如果您想创建一个类,则需要使用
class
关键字来执行此操作,因为这将创建一个不仅仅是类型的运行时值:
class Point{
constructor(public x: number, public y: number){}
}
let p = new Point(10,10)
您使用
type
(或在其他情况下使用 interface
)作为类型注释来指示 JavaScript 对象的结构:
type Point = {
x: number, y: number
}
function doSomething(p: Point) {
}
let p: Point = { x: 10, y: 15 };
doSomething(p);
这些类型注释受结构类型的影响,这意味着在这种特定情况下,您可以删除类型注释:
let p = { x: number, y: number };
doSomething(p);
类是完全不同的东西,它为您提供了 JS 原型继承的更优雅的替代方案:
class Point {
public x: number;
public y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
public move(deltaX: number, deltaY: number) {
this.x = this.x + deltaX;
this.y = this.y + deltaY;
}
}
let p = new Point(10, 15);
p.move(1, 2);
当您查看生成的 JS 代码时,您很快就会注意到差异:
type
声明已删除在生成的代码中。
为了更好地理解,我为所有差异创建了一个表格:
| Description | Type | Interface |
|---------------------------------------------------------|------|-----------|
| Describe functions | ✔ | ✔ |
| Describe constructors | ✔ | ✔ |
| Describe tuples | ✔ | ✔ |
| Interfaces can extend | ≈ | ✔ |
| Classes can extend | ✘ | ✔ |
| Classes can implement | ≈ | ✔ |
| Divide another one of its own kind | ✔ | ≈ |
| Create a union with another one of its own kind | ✔ | ✘ |
| Be used to create mapped types | ✔ | ✘ |
| Be mapped over with mapped types | ✔ | ✔ |
| Expands in error messages and logs | ✔ | ✘ |
| Be completed | ✘ | ✔ |
| Be recursive | ≈ | ✔ |
Hint: `≈` means partially
也许对于新版本的
TypeScript
,此表格有一些更改,如果有请通过编辑当前帖子来修复表格。
class myClass extends myParentClass implements myInterface{
}
let value = new myClass();
// value is from type any and from type myClass and from type myParentClass and from type myInterface. Its class is myClass
new 只能与类名一起使用。但 Point 不是一个类名,而是一个值。
class Point{
private x;
private y;
constructor(x,y) {
this.x = v1;
this.y = v2;
}
}
现在 Point 是一个类,你可以这样做:
let p = new Point(1,2);