Configuration
Enable noImplicitAny
and strictNullChecks
Primitive types
Numbers
let num: number;
num = 123;
num = 123.456;
num = '123'; // Error
Interfaces
interface Person {
firstName: string;
middleName?: string; // optional member
lastName: string;
age: number;
}
// OK
let person1: Person;
person1 = {
firstName: "John",
// not an error, because middleName is optional
lastName: "Doe",
age: 42,
};
// Error
person1 = {
firstName: "John",
lastName: "Doe",
age: "42", // Error! wrong type
};
// Error
person1 = {
firstName: "John",
middleName: "V",
// Error! lastName is missing
age: 42,
};
Booleans
let bool: boolean;
bool = true;
bool = false;
bool = 'false'; // Error
bool = 1; // Error
Strings
let str: string;
str = '123';
str = 123; // Error
Arrays
let strArray: string[];
strArray = ["hello", "world"];
console.log(strArray[0]); // 'hello'
strArray[1] = "lafayette"; // OK
strArray = ["goodbye", "cruel", "world"]; // OK
strArray[0] = false; // Error!
strArray = "hello world"; // Error!
strArray = [42, "world"]; // Error!
Strict Object Literal Checking
function logName(something: { name: string }) {
console.log(something.name);
}
var person = { name: "ryan", job: "being awesome" };
var animal = { name: "cow", diet: "vegan" };
var random = { note: "I don't have a name property" };
logName(person); // OK
logName(animal); // OK
logName(random); // Error: property `name` is missing
let myObject: {
mustBeNumber: number;
[index: string]: any; // index signature!
};
// OK, `extra` matched by index signature
myObject = { mustBeNumber: 1, extra: true };
// Error!, `mustBeNumber` must be a number
myObject = { mustBeNumber: true, extra: 'a string' };
Classes & Interfaces
interface Point {
x: number;
y: number;
add: () => number;
}
class MyPoint implements Point {
x: number;
y: number;
// etc
add() {
return this.x + this.y;
}
}
any
let vehicle: any;
// Takes all types
vehicle = '123';
vehicle = 123;
vehicle = { make: 'ford', model: 'bronco' };
// Is compatible with all types
let num: number;
vehicle = num;
num = vehicle;
null, undefined & void
let mileage: number;
let model: string;
// These literals can be assigned to any type
mileage = null;
mileage = undefined;
model = null;
model = undefined;
// void signifies that the function returns nothing
function logger(message: string): void {
console.log(message);
}
Unions
// this function accepts a string OR an array of strings
function formatCommandline(command: string | string[]) {
let line: string;
if (typeof command === "string") {
line = command.trim();
} else {
line = command.join(" ").trim();
}
// Do stuff with line, which is always string
}
Type Aliases
type Coordinates = { lat: number; long: number };
let myLocation: Coordinates;
// OK
myLocation = { lat: 48.423, long: -89.74 };
// Error!
myLocation = { lat: 48.423, long: null };
Functions
function doStuff(word: string, numRepeats: number): string {
return word.repeat(numRepeats);
// return numRepeats; // Error!
// return false; // Error!
}
doStuff("AcadianaSoftwareGroup", 5); // OK
doStuff("AcadianaSoftwareGroup"); // Error!
doStuff(42, 3); // Error!
interface Person {
fullName: string;
age: number;
}
function makePerson(firstName: string, lastName: string, age: number, salutation?: string): Person {
return {
fullName: `${salutation || ""} ${firstName} ${lastName}`.trim(),
age: age,
};
// Error!
// return {
// firstName: firstName,
// age: age,
// };
}
const jWright = makePerson("Johnathon", "Wright", 28); // OK
const godfather = makePerson("Chris", "Parich", 24, "Sir"); // OK
const dSmith = makePerson("Davin", 34, "Smith"); // Error!
const aSonge = makePerson("Alex", "Songe"); // Error!
Function Overloads
function padding(a: number, b?: number, c?: number, d?: any) {
if (b === undefined && c === undefined && d === undefined) {
b = c = d = a;
} else if (c === undefined && d === undefined) {
c = a;
d = b;
}
return {
top: a,
right: b,
bottom: c,
left: d,
};
}
// Overloads
function padding(all: number);
function padding(topAndBottom: number, leftAndRight: number);
function padding(top: number, right: number, bottom: number, left: number);
// Function body
function padding(a: number, b?: number, c?: number, d?: number) {
if (b === undefined && c === undefined && d === undefined) {
b = c = d = a;
} else if (c === undefined && d === undefined) {
c = a;
d = b;
}
return {
top: a,
right: b,
bottom: c,
left: d,
};
}
padding(1); // OK: all
padding(1, 1); // OK: topAndBottom, leftAndRight
padding(1, 1, 1, 1); // OK: top, right, bottom, left
padding(1, 1, 1); // Error: Not a part of the available overloads
DefinitelyTyped
$ npm install —save @types/lodash
After that, use the library as you would normally, but with type safety!
import * as _ from "lodash";
Type Inference: Variables
// mileage is INFERRED to be a number
let mileage = 45692;
// make is INFERRED to be a string
let make = "Tesla";
mileage = make; // Error! cannot assign `string` to a `number`
Type Inference: Return Type
// inferred as returning a number
function add(a: number, b: number) {
return a + b;
}
// inferred as returning a string
function doStuff(word: string, numRepeats: number) {
return word.repeat(numRepeats);
}
Type Inference: Structuring
// objects
let foo = {
a: 123,
b: 456,
};
foo.a = "hello"; // Error! cannot assign `string` to a `number`
// arrays
const bar = [1, 2, 3];
bar[0] = "hello"; // Error! cannot assign `string` to a `number`
Enums
enum SocialNetworkType {
Twitter,
Facebook,
Google,
}
// Sample Usage
let type = SocialNetworkType.Facebook;
// Safety
type = 'not in enum'; // Error!
Enums are number based
enum SocialNetworkType {
Twitter, // 0
Facebook, // 1
Google, // 2
}
let type = SocialNetworkType.Facebook;
// Effectively the same as SocialNetworkType.Facebook
type = 1;
String enums
export enum SocialNetworkType {
Twitter = "twitter",
Facebook = "facebook",
Google = "google",
}
let type = SocialNetworkType.Google;
type = SocialNetworkType.Facebook; // OK
type = 'facebook'; // Error!
class Queue {
private data = [];
push = item => this.data.push(item);
pop = () => this.data.shift();
}
// Usage
const queue = new Queue();
queue.push("hello");
queue.push("world");
queue.pop(); // 'hello'
queue.pop(); // 'world'
const queue = new Queue();
queue.push(1);
queue.push("42"); // oops, we made a mistake
// sometime later...
queue.pop().toPrecision(1);
queue.pop().toPrecision(1); // RUNTIME ERROR
Generics
class Queue<T> {
private data = [];
push = (item: T) => this.data.push(item);
pop = (): T => this.data.shift();
}
// Usage
const queue = new Queue<number>();
queue.push(0);
queue.push("1"); // Error! cannot push a string. Only numbers allowed
Type Assertions
interface Person {
age: number;
name: string;
}
let mike = {};
mike.age = 54; // Error: property 'age' does not exist on `{}`
mike.name = "Michal"; // Error: property 'name' does not exist on `{}`
// OK with type assertion
let joe = {} as Person;
joe.age = 37;
joe.name = "Joseph";
Type assertion is dangerous
interface Person {
age: number;
name: string;
}
let joe = {} as Person; // #yolo
// sometime later...
joe.name.toLowerCase(); // RUNTIME ERROR!
Type Guards
function doCoolStuff(numberOrString: number | string) {
// typeof type guard
if (typeof numberOrString === "string") {
// Within the block TS knows that x is a string
console.log(Math.round(numberOrString)); // Error!
console.log(numberOrString.toLowerCase()); // OK
}
// Error! Ouside the block, there is no guarantee that x is a `string`
console.log(numberOrString.toLowerCase(1));
}
class Person {
ethnicity: "white";
lives = 1;
}
class Cat {
breed: "tabby";
lives = 9;
}
function doCoolStuff(arg: Person | Cat) {
// instanceof with else type guard
if (arg instanceof Person) {
console.log(arg.ethnicity); // OK
console.log(arg.breed); // Error!
} else {
// TS knows this MUST be Cat
console.log(arg.ethnicity); // Error!
console.log(arg.breed); // OK
}
}
class Person {
kind: "person";
ethnicity: "white";
lives = 1;
}
class Cat {
kind: "cat";
breed: "tabby";
lives = 9;
}
function doCoolStuff(arg: Person | Cat) {
// Literal type guard
if (arg.kind === "person") {
console.log(arg.ethnicity); // OK
console.log(arg.breed); // Error!
} else {
// TS knows this MUST be Cat
console.log(arg.ethnicity); // Error!
console.log(arg.breed); // OK
}
}
class Person {
ethnicity: "white";
lives = 1;
}
class Cat {
breed: "tabby";
lives = 9;
}
function isPerson(arg: any): arg is Person {
return arg.ethnicity !== undefined;
}
function doCoolStuff(arg: Person | Cat) {
// user-defined type guard
if (isPerson(arg)) {
console.log(arg.ethnicity); // OK
console.log(arg.breed); // Error!
} else {
// TS knows this MUST be Cat
console.log(arg.ethnicity); // Error!
console.log(arg.breed); // OK
}
}