What is the difference between type and class in Typescript?
Typescript has two different universes that come into contact in some points: Value space and Type space. Type space is where types are defined and types get erased completely and don't exist at runtime. Value space contains values and will obviously exist at runtime.
What is a value? Value literals, variables, constants and parameters are obviously values. Functions and class declarations are also values as they do have a runtime object backing them up, namely the function object and the class constructor (also a function). Enums are also values as they are backed up by an object at runtime.
What is a type? Any definition with a type
keyword is a type as well as interfaces, class declarations and enums
You will notice I mentioned class declarations in both spaces. Classes exist in both type space, and value space. This is why we can use them both in type annotations (let foo: ClassName
) and in expressions (ex new ClassName()
).
Enums also span both worlds, they also represent a type we can use in an annotation, but also the runtime object that will hold the enum.
Names in type space and value space don't collide, this is why we can define both a type and a variable with the same name:
type Foo = { type: true }
var Foo = { value : true } // No error, no relation to Foo just have the same name in value space
Class declarations and enums, since they span both spaces will 'use up' the name in both spaces and thus we can't define a variable or a type with the same name as a class declaration or enum (although we can do merging but that is a different concept)
In your specific case, Point
is just a type, something we can use in type annotations, not something we can use in expressions that will need to have a runtime presence. In this case the type is useful as it allows the compiler to structurally check that the object literal is assignable to the Point
type:
let p: Point = { x: 10, y: 15 }; // OK
let p: Point = { x: 10, y: 15, z: 10 }; // Error
If you want to create a class, you will need to do that with the class
keyword, as that will create a runtime value that is not just a type:
class Point{
constructor(public x: number, public y: number){}
}
let p = new Point(10,10)
You use a type
(or in other cases an interface
) for type annotations to indicate the structure of JavaScript objects:
type Point = {
x: number, y: number
}
function doSomething(p: Point) {
}
let p: Point = { x: 10, y: 15 };
doSomething(p);
These type annotations are subject to structural typing, meaning that in this specific case you could drop the type annotation:
let p = { x: number, y: number };
doSomething(p);
A class is something entirely different, which provides you a more elegant alternative to JS prototype inheritance:
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);
When you look at the generated JS code, you will quickly notice the difference:
The type
declaration is dropped in the generated code.
The class definition is turned into a JS function with prototype inheritance
For better understanding, I create a table for all differences:
| 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
Maybe for the new versions of TypeScript
, this table has some changes, if it has please fix the table by editing the current post.