A primitive type is a number, bool, string or void.
function showSum(x, y){
alert(x + " + " + y + " = " + (x+y));
}
showSum(5, "7");This is an ordinary peice of JavaScript. But it doesn't work as intended.
function showSum(x : number, y:number){
alert(x + " + " + y + " = " + (x+y));
}
showSum(5, "7");By adding the type annotation, the programmer's intent is clearer, and the compiler can check if the right arguments are being passed.
Creating an interface for the argument to showSum:
interface Addends{
x:number;
y:number;
}
function showSum(a:Addends){
alert(a.x + " + " + a.y + " = " + (a.x + a.y));
}
showSum({y:2, x:5});An interface is a type that defines the property names expected on an object instance (and their types).
An interface can overload return types for different values:
interface Document {
createElement(tagName: string): HTMLElement;
createElement(tagName: 'canvas'): HTMLCanvasElement;
createElement(tagName: 'div'): HTMLDivElement;
createElement(tagName: 'span'): HTMLSpanElement;
// + 100 more
}var input = document.createElement("input");
var div = document.createElement("div");A class is also a type. There is an example of this later.
Typescript now supports Enumsenum Colour{
Red,
Green,
Blue
}
alert(""+Colour.Red);
alert(""+Colour[1]);There are a number of ways in which TypeScript helps reduce the amount of code you have to type:
Functions can be declared shorthand:
interface Addends{
x:number;
y:number;
}
var showSum = (a:Addends) => alert(a.x + " + " + a.y + " = " + (a.x + a.y));
showSum({y:2, x:5});You can stop typing "function" so much! Multiple arguments and statements are supported:
var alertBoth = (s1:string, s2:string) => {alert(s1);alert(s2);};Class declarations are a formal part of the language. TS generates the appropriate JS:
class Adder {
x : number;
y : number;
constructor(x:number, y:number){
this.x = x;
this.y = y;
}
getSum(){
return this.x + this.y;
}
showSum(show : (string)=>void){
show(this.x + " + " + this.y + " = " + this.getSum())
}
}
var a = new Adder(2, 3);
a.showSum(s => alert(s));This declared a class named "Adder" with the fields x and y, and a constructor that set these fields, and two methods, getSum and showSum. Note the equivalent javascript that TS generates.
The "module" keyword creates a namespace:
module Mathematics{
export class Adder {
x : number;
y : number;
constructor(x:number, y:number){
this.x = x;
this.y = y;
}
getSum(){
return this.x + this.y;
}
showSum(show : (string)=>void){
show(this.x + " + " + this.y + " = " + this.getSum())
}
}
}
var a = new Mathematics.Adder(2, 3);
a.showSum(s => alert(s));The compiler has an AMD mode too!
Typescript 0.9 adds support for generics. This behaves very similarly to C#, it even has generic constraints
Here is an example of generic constraints:
interface IEquatable<T> {
equivalent(other: T): boolean;
hashCode(): string;
}
class HashSet<T extends IEquatable<T>>{
hash: any = {};
add(item: T): void {
if (this.contains(item)) {
throw "Item already added";
}
var hashCode = item.hashCode();
var buckets: Array<T> = this.hash[hashCode];
if (!buckets) {
this.hash[hashCode] = buckets = [];
}
buckets.push(item);
}
contains(item: T): boolean{
var hashCode = item.hashCode();
var buckets: Array<T> = this.hash[hashCode];
return buckets && buckets.some(x => item.equivalent(x));
}
}
class Person implements IEquatable<Person>{
constructor(public firstName: string, public lastName: string) {
}
equivalent(other:Person) {
return this.lastName == other.lastName;
}
hashCode() {
return this.lastName;
}
}
var s = new HashSet<Person>();
s.add(new Person("Bill", "Clinton"));
s.add(new Person("Hillary", "Clinton"));There are no formal abstract classes or methods in TypeScript, but you can create a method whose base implementation throws.
There are properties and "private" fields:
class Adder {
private x : number;
private y : number;
constructor(x:number, y:number){
this.x = x;
this.y = y;
}
get sum(){
return this.x + this.y;
}
showSum(show : (string)=>void){
show(this.x + " + " + this.y + " = " + this.sum)
}
}
var a = new Adder(2, 3);
//compiler warning, runtime set value
a.x = -10;
a.showSum(s => alert(s));Classes can extend other classes:
class Operation {
x : number;
y : number;
operatorText:string;
constructor(x:number, y:number, operatorText:string){
this.x = x;
this.y = y;
this.operatorText = operatorText;
}
operate():number{
throw "abstract method not implemented";
}
showOperation(show : (string)=>void){
show(this.x + " " + this.operatorText + " " + this.y + " = " + this.operate())
}
}
class Subtractor extends Operation {
constructor(x:number, y:number){
super(x, y, "-");
}
operate(){
return this.x - this.y;
}
}
var a = new Subtractor(11, 4);
a.showOperation(s => alert(s));This created a class named Operation, which holds some fields and knows how to show itself, and a class named Subtractor which extended Operation to provide specific implementation details.
IMO, one of the most disappointing aspects of TS is how you can't use another framework's own special brand of inheritance.
Polymorphism can be class based:
class Operation {
x : number;
y : number;
operatorText:string;
constructor(x:number, y:number, operatorText:string){
this.x = x;
this.y = y;
this.operatorText = operatorText;
}
operate():number{
throw "abstract method not implemented";
}
showOperation(show : (string)=>void){
show(this.x + " " + this.operatorText + " " + this.y + " = " + this.operate())
}
}
class Subtractor extends Operation {
constructor(x:number, y:number){
super(x, y, "-");
}
operate(){
return this.x - this.y;
}
}
function alertOperation(o:Operation){
o.showOperation(s => alert(s));
}
alertOperation(new Subtractor(11, 4));The alertOperation function can take any class that inherits from Operation, and we're providing it with a Subtractor.
Polymorphism can be interface based:
interface CanShowOperation{
showOperation(show : (string)=>void);
}
class Operation implements CanShowOperation {
x : number;
y : number;
operatorText:string;
constructor(x:number, y:number, operatorText:string){
this.x = x;
this.y = y;
this.operatorText = operatorText;
}
operate():number{
throw "abstract method not implemented";
}
showOperation(show : (string)=>void){
show(this.x + " " + this.operatorText + " " + this.y + " = " + this.operate())
}
}
class Subtractor extends Operation {
constructor(x:number, y:number){
super(x, y, "-");
}
operate(){
return this.x - this.y;
}
}
function alertOperation(o:CanShowOperation){
o.showOperation(s => alert(s));
}
alertOperation(new Subtractor(11, 4));The alertOperation function can take any object that implements the properties of the CanShowOperation interface, and we're providing it with a Subtractor. Note that Operation doesn't have to explicitly implement CanShowOperation.
Type Definition files (*.d.ts) are for holding definitions but not implementations. They are usually used to create TS definitions for existing JS code. For example, here's the type definitions for Underscore.
It's often handy to pull in some definition files for the library you're using. Definitely Typed is a good one.