Understanding TypeScript
Opened this issue · 0 comments
genkio commented
study notes taken from the Understanding TypeScript course
Installation
npm -g install typescript
Initialize project
Create tsconfig.json
tsc --init
Types
Basic type
let myName:string = 'Joe';
Array that has elements of type any
let hobbies: any[] = ['Coding', 'Reading'];
Tuple
let address: [string, number] = ['Some address', 1];
Enum
enum Color { Blue, Grey, Green };
let myColor: Color = Color.Blue;
console.log(myColor); // 0
enum myColor { Blue = 100, Grey = 200, Green = 300 };
Function with return type
function returnMyName(): string {
return 'Joe';
}
Function with no return value
function returnNothing: void {
console.log('Hello');
}
Function with argument types
funciton multiply(value1: number, value2: number): number {
return value1 * value2;
}
Function types
let myMultiply: (a: number, b: number) => number
myMultiply = multiply;
Objects and types
let userData: { name: string, age: number } = {
name: 'Joe',
age: 30
};
Complex object types
let complex: { data: number[], output: (all: boolean) => number[] } = {
data: [100, 3.99, 10],
output: function (all: boolean): number[] {
return this.data;
}
};
Type alias
type Complex = { data: number[], output: (all: boolean) => number[] }
let complex: Complex = {
data: [100, 3.99, 10],
output: function (all: boolean): number[] {
return this.data;
}
};
Union types
let myAge: number | string = 30;
myAge = '30';
Check types
let value = 30;
if (typeof value == 'number') {
// do something about it
}
Never type
function neverReturn(): never {
throw new Error('An error!');
}
Nullable types
let canBeNull: number | null = 12;
canBeNull = null;
Classes
Basics
class Person {
name: string;
private type: string;
protected age: number = 30;
constructor(name: string, public username: string) {
this.name = name;
}
printAge() {
console.log(this.age);
}
// only way to write private variable
setType(type: string) {
this.type = type;
console.log(this.type);
}
}
const person = new Person('Joe', 'joe');
console.log(person); // { name: 'Joe', username: 'joe' }
person.printAge();
person.setType('Cool guy');
Inheritance
class Joe extends Person {
constructor(username: string) {
super('Doe', username);
this.age = 31;
}
}
const doe = new Joe('doe')
console.log(doe);
Getters & setters
class Plant {
private _species: string;
get species() {
return this._species;
}
set species(value: string) {
if (value.length > 3) {
this._species = value;
} else {
this._species = 'Default';
}
}
}
let plant = new Plant();
plant.species = 'Green Plant';
Static properties & methods
class Helpers {
static PI: number = 3.14;
static calcCircumference(diameter: number): number {
return this.PI * diameter;
}
}
console.log(2 * Helpers.PI);
console.log(Helpers.calcCircumference(8));
Abstract classes
abstract class Project {
projectName: string = 'Default';
budget: number;
abstract changeName(name: string): void;
calcBudget() {
return this.budget * 2;
}
}
class ITProject extends Project {
changeName(name: string): void {
this.projectName = name;
}
}
const newProject = new ITProject();
Implement singleton with private constructors
class OnlyOne {
private static instance: OnlyOne;
private constructor(public name: string) {}
static getInstance() {
if (!OnlyOne.instance) {
OnlyOne.instance = new OnlyOne('The Only One');
}
return OnlyOne.instance;
}
}
const ins = OnlyOne.getInstance();
Readonly property
class OnlyOne {
private static instance: OnlyOne;
public readonly name: string;
private constructor(name: string) {
this.name = name;
}
static getInstance() {
if (!OnlyOne.instance) {
OnlyOne.instance = new OnlyOne('The Only One');
}
return OnlyOne.instance;
}
}
const ins = OnlyOne.getInstance();
ins.name = 'error'; // compile error
Namespaces and Modules
Namesapces
namespace MyMath {
const PI = 3.14;
export function calcCircumference(diameter: number) {
return diameter * PI;
}
}
console.log(MyMath.calcCircumference(10))
Import namespaces
/// <reference path="circleMath.ts" />
Nested namespaces
namespace MyMath {
export namespace Circle {
const PI = 3.14;
export function calcCircumference(diameter: number) {
return diameter * PI;
}
}
}
console.log(MyMath.Circle.calcCircumference(10))
// or use shortcut
import CircleMath = MyMath.Circle;
console.log(CircleMath.calcCircumference(10))
Modules
// circle.ts
export const PI = 3.14;
export function calcCircumference(diameter: number) {
return diameter * PI;
}
// index.ts
import { PI, calcCircumference } from './math/circle';
Interfaces
interface NamedPerson {
firstName: string;
age?: number;
[propName: string]: any;
greet(lastName: string): void;
}
function greet(person: NamedPerson) {
console.log('Hello ', person.firstName);
}
const person: NamedPerson = {
firstName: 'Joe',
hobbies: ['Cooking', 'Reading'],
greet(lastName: string) {
console.log('Hi, I\'m ' + this.firstName + ' ' + lastName);
}
};
greet(person);
person.greet('Doe');
Implement an interface
class Person implements NamedPerson {
firstName: string;
greet(lastName: string) {
console.log('I\'m ' + lastName);
};
}
const myPerson = new Person();
myPerson.firstName = 'Joe';
myPerson.greet('Doe');
Interface for function type
interface DoubleValueFunc {
(number1: number, number2: number): number;
}
let myDoubleFunction: DoubleValueFunc;
myDoubleFunction = function(value1: number, value2: number) {
return (value1 + value2) * 2;
};
Interface inheritance
interface AgedPerson extends NamedPerson {
age: number;
}
const oldPerson: AgedPerson = {
age: 30,
firstName: 'Joe',
greet(lastName: string) {
console.log('Hello');
}
};
Generics
function echo<T>(data: T) {
return data;
}
console.log(echo('Joe').length);
console.log(echo(30).length); // compile error
Built-in generics
const results: Array<number> = [1.94, 2];
results.push(-2.99);
results.push('string'); // compile error
function printAll<T>(args: T[]) {
args.forEach((element) => console.log(element));
}
printAll<string>(['Apple', 'Orange']);
Generic types
const echo2: <T>(data: T) => T = echo;
console.log(echo2<string>('Something'));
Generic class
class SimpleMath<T extends number> {
baseValue: T;
multipleValue: T;
calculate(): number {
return this.baseValue * this.multipleValue;
}
}
const simpleMath = new SimpleMath<number>();
simpleMath.baseValue = 'something'; // compile error
simpleMath.multipleValue = 20;
console.log(simpleMath.calculate());
Use more than one generic types
class SimpleMath<T extends number | string, U extends number | string> {
baseValue: T;
multipleValue: U;
calculate(): number {
return this.baseValue * this.multipleValue;
}
}
const simpleMath = new SimpleMath<string, number>();
simpleMath.baseValue = '10';
simpleMath.multipleValue = 20;
console.log(simpleMath.calculate());
Put it together
class MyMap<T> {
private map: {[key: string]: T} = {};
setItem(key: string, item: T) {
this.map[key] = item;
}
getItem(key: string) {
return this.map[key];
}
clear() {
this.map = {};
}
printMap() {
for (let key in this.map) {
console.log(key, this.map[key]);
}
}
}
const numberMap = new MyMap<number>();
numberMap.setItem('apples', 10);
numberMap.setItem('oranges', 2);
const stringMap = new MyMap<string>();
stringMap.setItem('apples', '10');
stringMap.setItem('oranges', '2');
Decorators
function logged(constructorFn: Function) {
console.log(constructorFn);
}
@logged
class Person {
constructor() {
console.log('Hi');
}
}
Decorator factory
function logging(value: boolean) {
return value ? logged: null;
}
@logging(true)
class Car {
constructor() {
console.log('Hi');
}
}
Use decorator to enrich class instance
function printable(constructorFn: Function) {
constructorFn.prototype.print = function() {
console.log(this);
}
}
@printable
class Plant {
name = 'Green Plant';
}
const plant = new Plant();
(<any>plant).print();
Method decorator
function editable(value: boolean) {
return function(target: any, propName: string, descriptor: PropertyDescriptor) {
descriptor.writable = value;
}
}
class Project {
projectName: string;
constructor(name: string) {
this.projectName = name;
}
@editable(false)
calcBudget() {
console.log(1000);
}
}
const project = new Project('Super Project');
project.calcBudget();
project.calcBudget = function() {
console.log('2000'); // will not work (edit)
}
project.calcBudget();
Parameter decorator
function printInfo(target: any, methodName: string, paramIndex: number) {
console.log('Hello');
}
class Course {
name: string;
constructor(name: string) {
this.name = name;
}
printStudentNumbers(mode: string, @printInfo printAll: boolean) {
if (printAll) {
console.log(1000);
} else {
console.log(2000);
}
}
}
const course = new Course();
course.printStudentNumbers('anything', true);
course.printStudentNumbers('anything', false);