If you have used ES6/ES2015 with Babel, you will feel right at home with TypeScript. Babel and TypeScript both are transpilers to generate ES5 compatible JavaScript, but TypeScript goes beyond that. TypeScript provides comprehensive typechecker and additional abstractions not part of the ECMA Script specification. If you want strong type system and features like generics, interfaces, decorators, etc. TypeScript is probably what you want to go with. Tooling and developer experience is also another aspect - typecheker enables tooling to give instant feedback on type mismatch or violations. Here are few quick reference notes on TypeScript.
Classes, Abstract Classes and Inheritance
TypeScript classes are mostly same as ES6 with additional features like static properties and abstract classes.
class Contact {
name: string; //if no accessor is specified, it's treated as public
public phoneNumber: string; //to be explicit, you cold specify public
private companyName: string; //accessible only within the class
public static contactType = "Institutional"; //static property accessible at the class-level
//method with param and return types
getVCard(formatVersion: string):string {
return "ToDo: Not implemented";
}
}
Let’s create an abstract class with few common properties and an abstract method which needs to be implemented by any class inheriting from it.
abstract class Entity {
id: number;
createdBy: string;
createdOn: Date;
modifiedOn: Date;
abstract toText():string;
}
Now, we could inherit the Contact
from this class, we will have to implement the toText
method.
class Contact extends Entity {
name: string; //if no accessor is specified, it's treated as public
public phoneNumber: string; //to be explicit, you cold specify public
private companyName: string; //accessible only within the class
public static contactType = "Institutional"; //static property accessible at the class-level
//method with param and return types
getVCard(formatVersion: string):string {
return "ToDo: Not implemented";
}
toText():string {
return `${this.name} works with ${this.companyName}`;
}
}
Interfaces
Interfaces is a nice feature - it could contain the properties and methods. Properties could be flagged as optional.
interface ExportProvider {
format: string;
version?: number; //'?' flags this property as optional
export(data): string;
}
class ExcelExport implements ExportProvider {
format: string = "Excel";
//version: number = 1; //we have flagged at optional, so this isn't required
export(data): string {
return "ToDo: implement the export";
}
}
Optional and Default Parameters
TypeScript allows to flag parameters to be optional - in that case the value will be undefined
. When it’s defaulted, it will assume the assigned value when not passed.
function downloadDocument(id: number, compress: boolean = false, fileName?: string) {
console.log(`id=${id}, compress=${compress}, fileName=${fileName}`);
}
downloadDocument(101);
downloadDocument(101, true);
downloadDocument(101, true, "docs.zip");
Rest Parameters
Sometimes the number of argument to be passed could vary, rest parameters
allows that flexibility. You could specify the types for these parameters, if you no type is specified, the caller could pass the parameters of any type.
//'topics' here is the rest argument of type number
function subscribe(email: string, ...topics:number[]): boolean{
topics.forEach(t => console.log(t));
return true;
}
subscribe('someone@nowhere.com', 102, 223, 424);
//'inputs' is here rest argument without any type requirement
function rankInputs(logicType: string, ...inputs) {
inputs.forEach(i => console.log(i));
}
rankInputs("fuzzy", true, 101, { percentile: 98.3 });
Constructor Param Properties
You could avoid the boiler plate of accepting the parameters in the constructor and assigning it to the property by simply assigning an accessor to the argument.
class Engine {
constructor(private id: string, public name:string, public capacity: number) {
//'id' will be accessible only within this class
//'name' and 'capacity' will be exposed as a public properties
}
}
Generics and Constrains
If you have used generics in C# or Java, you know it brings lots of flexibility and reusability to the design. TypeScript generics has very similar feel to C# generics. It supports generic class, generic function and generic interfaces as well.
Generic Function
export function ReadValue<T>(row, column): T {
//return value must be of type T
}
Generic Class
class ModelHelper<T> {
validate(model: T) {
}
createInstance(model: T): T {
//return type needs to be of type T
}
}
Generic Interface
Same way you could define the interface with the generic, classes implementing the interface will have to implement the generic as well.
interface Repository<T> {
Save(T): T;
Delete(number): boolean;
Get(number): T;
}
Constrains
You could define the contains around the type of the generic which could be used with the function, class or interface. Let’s redefine the Repository
interface from the previous example to enforce a type requirement on generic type T
. Keyword extends
mandates that the type used with this generic interface must be of type Entity
.
interface Repository<T extends Entity> {
Save(T): T;
Delete(number): boolean;
Get(number): T;
}