TypeScript is a language designed by Microsoft in order to provide JavaScript developers with some automatic error checking (and code suggestions) at implementation time, without the need for running the code (static analysis). These features are built upon class definitions and type declaration for variables. TypeScript also emulates modern JavaScript features such as those defined in ECMAScript 6.
This guide will briefly present TypeScript (version 2), skipping some basic concepts (interfaces, encapsulation, inheritance, polymorphism, overriding, static members) that are familiar to every developer who has class-based object-oriented programming knowledge. Also, ECMAScript 6 features won't be discussed, as they are treated in another guide.
TypeScript compiles (more precisely... "transpiles") into regular JavaScript and that's why you should follow this guide after installing the official compiler. Alternatively, you can use an online tool. When you will need to get more serious, try also Visual Studio Code or another IDE.
null
e undefined
)Examples:
let n: number = 7; // OK
let s: string = 'hello'; // OK
let b: boolean = true; // OK
let x: void = null; // OK
let y: any = 'thing'; // OK
n = '7'; // Error(string assigned to number)
s = 42; // Error (number assigned to string)
b = 'true'; // Error (string assigned to boolean)
x = 'foo'; // Error (not null, not undefined)
You can omit types and rely on type inference:
let n = 7; // (type number inferred)
n = 'hello'; // Error (string assegnata a number)
Nota: Uninitialized variables with no type declaration (eg. let x;
) are
considered any
variables.
Arrays can be declared like this:
let n: number[];
or:
let n: Array<number>;
TypeScript arrays can only store values of the declared type:
let n: number[];
n = [ 1, 2, 3 ]; // OK
n = [ '1', '2', '3' ]; // Error
If you need to store different types in the same array (just like in JavaScript) you can use tuples:
let rating: [ string, number ];
rating = [ 'Matrix', 5 ]; // OK
rating = [ 'Sharknado, '1' ]; // Error
The type inference systems supports arrays too:
let n = [ 1, 2, 3 ]; // number[]
You can specify parameter types and return type:
function sum(a: number, b: number): number {
return a + b;
}
sum(2, 2); // 4
sum('2', '2'); // Error
It's usually safe to omit the return type and rely on type inference:
function sum(a: number, b: number) {
return a + b;
}
let s: string = sum(2, 2); // Error (number assigned to string)
Interfaces allow to defined object types in terms of expected properties:
interface Person {
name: string;
age: number;
}
Remember that properties can also be methods:
interface Calculator {
sum(a: number, b: number): number;
multiply(a: number, b: number): number;
...
}
Declared properties are implicitly required:
interface Person {
name: string;
age: number;
}
const marco: Person = { name: 'Marco', age: 30 }; // OK
const johnDoe: Person = { age: 33 }; // Error (name missing)
...but you can mark them as optional:
interface Person {
name?: string;
age: number;
}
const marco: Person = { name: 'Marco', age: 30 }; // OK
const johnDoe: Person = { age: 33 }; // OK
You can't use unexpected properties:
interface Person {
name?: string;
age: number;
}
let marco: Person = { name: 'Marco', age: 30 }; // OK
marco = { name: 'Marco', age: 30, job: 'developer' }; // Error
You can temporarily treat a variable as if it had a different type:
interface Person {
name: string;
}
interface Athlete {
name: string;
sport: string;
}
const marco: Person = { name: 'Marco' };
marco.sport = 'soccer'; // Error
(marco as Athlete).sport = 'soccer'; // OK
There's also an alternative syntax:
(<Athlete>marco).sport = 'soccer';
You can define classes and declare their expected istance variables:
class Person {
name: string;
}
const marco = new Person();
marco.name = 'Marco'; // OK
marco.job = 'Developer'; // Error
You can define a custom constructor:
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
const marco = new Person('Marco');
console.log(marco.name); // 'Marco'
You can define one or more instance methods:
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
sayHi(): string {
return `Hi I'm ${this.name}`;
}
}
const marco = new Person('Marco');
marco.sayHi(); // 'Hi I'm Marco'
TypeScript classes compile into JavaScript constructor functions. For instance, this code:
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
const marco = new Person('Marco');
...would be translated into something like:
function Person(name) {
this.name = name;
}
var marco = new Person('Marco');
The implementing class must contain every property declared inside the interface:
interface HasName {
name: string;
}
interface CanTalk {
saySomething(what: string);
}
class Person implements HasName, CanTalk {
name: string;
saySomething(what: string) {
console.log(what);
}
}
class Person {
private name: string;
constructor(name: string) {
this.name = name;
}
}
const marco = new Person('Marco');
console.log(marco.name); // Error (private variable)
You can also declare protected
instance variables, which makes them directlty
accessible inside subclasses.
You can encapsulate methods too:
class Example {
private aPrivateMethod() {
console.log('OK');
}
aPublicMethod() {
this.aPrivateMethod();
}
}
const example = new Example();
example.aPrivateMethod(); // Error (private method)
example.aPublicMethod(); // 'OK'
You can also declare readonly
instance variables, which can be assigned only
inside constructors:
class Person {
readonly name: string;
constructor(name: string) {
this.name = name;
}
changeName(newName: string) {
this.name = newName; // Error
}
}
const marco = new Person('Marco');
console.log(marco.name); // 'Marco';
marco.name = 'Mario'; // Error
Static members are attributes or methods of a class. When a TypeScript class is compiled, static members become properties of the resulting JavaScript constructor function. For instance, this code:
class Counter {
static n: number;
static initialize() {
Counter.n = 0;
}
}
...translates into something like this:
function Counter() {}
Counter.initialize = function () {
Counter.n = 0;
};
class Person {
protected name: string;
constructor(name: string) {
this.name = name;
}
getName() {
return this.name;
}
}
class Athlete extends Person {
protected sport: string;
constructor(name: string, sport: string) {
super(name);
this.sport = sport;
}
getNameAndSport() {
return `${this.name} ${this.sport}`;
}
}
const marco: Person = new Athlete('Marco', 'Soccer');
marco.getName(); // 'Marco'
marco.getNameAndSport(); // Errore
(marco as Athlete).getNameAndSport(); // 'Marco Soccer'
class Person {
protected name: string;
constructor(name: string) {
this.name = name;
}
sayHi() {
return `Hi I'm ${this.name}`;
}
}
class Athlete extends Person {
protected sport: string;
constructor(name: string, sport: string) {
super(name);
this.sport = sport;
}
sayHi() {
return `Hi I'm ${this.name} and I play ${this.sport}`;
}
}
const marcoPerson = new Person('Marco');
const marcoAthlete = new Athlete('Marco', 'soccer');
marcoPerson.sayHi(); // 'Hi I'm Marco'
marcoAthlete.sayHi(); // 'Hi I'm Marco and I play soccer'
abstract class Shape {
abstract area(): number;
}
class Square extends Shape {
side: number;
constructor(side: number) {
super();
this.side = side;
}
area() {
return this.side * this.side;
}
}
const square: Shape = new Square(8);
square.area(); // 64
You can define generic functions:
function identity<T>(x: T): T {
return x;
}
identity<number>(7); // 7
identity<number>('7'); // Error
identity<string>('7'); // '7'
identity('7'); // '7' (type string inferred)
...or even generic classes:
class IdentityCalculator<T> {
apply(x: T): T {
return x;
}
}
const numberIdentity = new IdentityCalculator<number>();
numberIdentity.apply(7); // 7
numberIdentity.apply('7'); // Error
const stringIdentity = new IdentityCalculator<string>();
stringIdentity.apply(7); // Error
stringIdentity.apply('7'); // '7'