TypeScript is a superset of JavaScript, so the “class” keyword can be used as a type and value at runtime.
Differences between Type and Interface
Declaration merging (augmentation):
Interfaces support declaration merging, which means that you can define multiple interfaces with the same name, and TypeScript will merge them into a single interface with the combined properties and methods. On the other hand, types do not support declaration merging. This can be helpful when you want to add extra functionality or customize existing types without modifying the original definitions or patching missing or incorrect types.
interface A { //Writing Type instead of interface causes error
x: string;
}
interface A { //Writing Type instead of interface causes error
y: string;
}
const j: A = {
x: 'xx',
y: 'yy',
};
Extending other types/interfaces:
Both types and interfaces can extend other types/interfaces, but the syntax is different. With interfaces, you use the extends
keyword to inherit properties and methods from other interfaces. However, an interface cannot extend a complex type like a union type.
interface A {
x: string;
y: number;
}
interface B extends A {
z: string;
}
const car: B = {
x: 'x',
y: 123,
z: 'z',
};
For types, you use the & operator to combine multiple types into a single type (intersection).
interface A {
x: string;
y: number;
}
type B = A & {
j: string;
};
const c: B = {
x: 'x',
y: 123,
j: 'j',
};
Union and Intersection Types
Types are more flexible when it comes to defining Union and Intersection Types. With the type
keyword, you can easily create union types using the |
operator and intersection types using the &
operator. While interfaces can also represent union types indirectly, they don’t have built-in support for intersection types.
type Department = 'dep-x' | 'dep-y'; // Union
type Person = {
name: string;
age: number;
};
type Employee = {
id: number;
department: Department;
};
type EmployeeInfo = Person & Employee; // Intersection
Example with interfaces:
interface A {
x: 'x';
}
interface B {
y: 'y';
}
type C = A | B; // Union of interfaces
class Animal {
constructor(public name: string) {}
}
class Dog extends Animal {
constructor(
public name: string,
public bark: () => void
) {
super(name);
}
}
class Cat extends Animal {
constructor(
public name: string,
public meow: () => void
) {
super(name);
}
}
type Mammal = Dog | Cat;
const makeNoise = (mammal: Mammal) => {
if (mammal instanceof Dog) {
mammal.bark();
} else {
mammal.meow();
}
};
const dog = new Dog('Fido', () => console.log('bark'));
makeNoise(dog);
In JavaScript, a “class” has a “prototype” property, and the “instanceof” operator can be used to test if the prototype property of a constructor appears anywhere in the prototype chain of an object.
TypeScript has no effect on runtime performance, as all types will be erased. However, TypeScript does introduce some build time overhead.
Future JavaScript => Now
TypeScript provides a number of features that are planned in ES6 for current JavaScript engines (that only support ES5 etc). The TypeScript team is actively adding these features and this list is only going to get bigger over time and we will cover this in its own section. But just as a specimen here is an example of a class:
class Point {
constructor(public x: number, public y: number) {
}
add(point: Point) {
return new Point(this.x + point.x, this.y + point.y);
}
}
var p1 = new Point(0, 10);
var p2 = new Point(10, 20);
var p3 = p1.add(p2); // { x: 10, y: 30 }
and the lovely fat arrow function:
var inc = x => x+1;
Table of Contents
Enums are comparable and valid with numbers and vice versa, but comparing Enum values from different Enum types is invalid.
[[Error_TS2367]]
enum X {
A,
B,
}
enum Y {
A,
B,
C,
}
const xa: number = X.A; // Valid
const ya: Y = 0; // Valid
X.A === Y.A; // Invalid
>Out:
error TS2367: This comparison appears to be unintentional because the types ‘X’ and ‘Y’ have no overlap.
Instances of a class are subject to a compatibility check for their private and protected members:
class X {
public a: string;
constructor(value: string) {
this.a = value;
}
}
class Y {
private a: string;
constructor(value: string) {
this.a = value;
}
}
let x: X = new Y('y'); // Invalid
The comparison check does not take into consideration the different inheritance hierarchy, for instance:
class X {
public a: string;
constructor(value: string) {
this.a = value;
}
}
class Y extends X {
public a: string;
constructor(value: string) {
super(value);
this.a = value;
}
}
class Z {
public a: string;
constructor(value: string) {
this.a = value;
}
}
let x: X = new X('x');
let y: Y = new Y('y');
let z: Z = new Z('z');
x === y; // Valid
x === z; // Valid even if z is from a different inheritance hierarchy
Generics are compared using their structures based on the resulting type after applying the generic parameter, only the final result is compared as a non-generic type.
interface X<T> {
a: T;
}
let x: X<number> = { a: 1 };
let y: X<string> = { a: 'a' };
x === y; // Invalid as the type argument is used in the final structure
interface X<T> {}
const x: X<number> = 1;
const y: X<string> = 'a';
x === y; // Valid as the type argument is not used in the final structure
When generics do not have their type argument specified, all the unspecified arguments are treated as types with “any”:
type X = <T>(x: T) => T;
type Y = <K>(y: K) => K;
let x: X = x => x;
let y: Y = y => y;
x = y; // Valid
Remember:
let a: number = 1;
let b: number = 2;
a = b; // Valid, everything is assignable to itself
let c: any;
c = 1; // Valid, all types are assignable to any
let d: unknown;
d = 1; // Valid, all types are assignable to unknown. Unknown is a type-safe as a alternative for 'any'
let e: unknown;
let e1: unknown = e; // Valid, unknown is only assignable to itself and any
let e2: any = e; // Valid
let e3: number = e; // Invalid
let f: never;
f = 1; // Invalid, nothing is assignable to never
let g: void;
let g1: any;
g = 1; // Invalid, void is not assignable to or from anything expect any
g = g1; // Valid
Please note that when “strictNullChecks” is enabled, “null” and “undefined” are treated similarly to “void”; otherwise, they are similar to “never”.