TypeScript
The future today! and more...
What is TypeScript?
CoffeeScript Dart Haxe Opa
A(nother) language that compiles to JavaScript
Superset?





Optional Type System!

Java Types

Crazy OOP diagrams



Aren't we already using Types?

Angular docs

React docs

HTMLInputElement on MDN


So what?!

Refactor
List parameters
Find occurrences
Go to definition
Code completion
inline errors


"JavaScript - the bad parts"
var one = 1;
var two = '2';
var three = one + two;
console.log(three);
"12"
false.toString();
"false"
[1,2,3].toString();
"1,2,3"
"2".toString();
"2"
2.toString();
Unexpected token ILLEGAL
02.toString();
"2"
2 .toString();
"2"
var settings = {
maxTax : 10
}
var product = {
price : 100,
tax : settings.maxTax/2,
discount : 20
};
var totalPrice = product.price + product.maxTax;
var afterDiscount = totalPrice - product.discount;
console.log(afterDiscount);
NaN
Basic Types
Basic Types
var x: string;
var y: number;
var z: boolean;
var foo: any;
var bar; // Same as "any"
var x;
var y;
var z;
var foo;
var bar;
Arrays
var a: [];
var b: string[];
var p: Product[];
var a;
var b;
var p;
function calcPrice(products: Products[]):number {
.
.
.
}
function calcPrice(products) {
.
.
.
}
Functions as types
var func : (name:string) => number;
function process(x: () => string){
x().toLowerCase();
}
var func;
function process(x){
x().toLowerCase();
}questions : string[];
Interfaces
Structural types
function process(x: {a:string; b:number}){
return x.a.length;
}interface IThing {
a: number;
b: string;
}
function process(x: IThing){
return x.a.length;
}
Interfaces
function process(x){
return x.a.length;
}
function process(x){
return x.a.length;
}
Structural vs Interface
interface IThing {
a: number;
b: string;
}
function process(x: IThing){
return x.b.length;
}
var n = process({a:10, b:'foo', c:'Extra field'});
function process(x){
return x.b.length;
}
var n = process({a:10, b:'foo', c:'Extra field'});Optional fields
interface IPerson {
age: number;
name: string;
address?: string; // <-- optional field
}
function getName(p: IPerson){
return p.name;
}
var name = getName({age:10, name:'Me'});Function fields
interface IPerson {
age: number;
name: string;
address: string;
walk(distance:number): number; // <-- a Function
}questions : IQuestion[];
Function overloads*
interface IPerson {
age: number;
name: string;
address: string;
walk(distance:number): number;
walk(destination:string): number;
walk(location:{x:number, y:number}): number;
}As a Function (Hybrid)*
interface IPerson {
age: number;
name: string;
address: string;
walk(distance:number): number;
(): string; // <-- a Function
}new(able)*
interface IHumanStatic {
MAX_AGE:number;
new():IHuman;
}array*
interface StringArray {
[index: number]: string;
}Type inference
Where inference takes over?
var x = 3; // x is a number
class MyClass {
name = "Foo"; // name is a string
}
function foo(value = false) { // value is a boolean
}
function calc() {
return 55; // calc returns a number
}
var x = calc(); // x is also a number
if (typeof x === "string") {
// x is a string
}
backward inference
interface IHuman {
age: number;
walk(distance:number):void;
}
var man : IHuman = {
age : 120,
walk: function(distance) {
console.log(distance); // distance inferred to be a number
}
}
backward inference #2
window.onmousedown = function(mouseEvent) {
// mouseEvent inferred as MouseEvent
console.log(mouseEvent.button);
};
Inference can cause errors
var x = 3; // x is a number
x = "45"; // compiler error
var foo = {};
foo.description = 'I am FOO'; // compiler error
var person : IPersonModel = {}; // compiler error
var x : any = 3; // x can be anything
x = "45";
var foo : any = {};
foo.description = 'I am FOO'; // compiler is happy
var person : IPersonModel = <IPersonModel>{};
Best common type
var x = [0, 1, null]; // x is number[]
var y = [0, 1, 'str']; // y is (string | number)[]
any
var x; // x is any forever
x = '45'; // x is still any
function process(x): { // x is any
return x+x*3; // return type is any
}
process(42); // this does not change the type of x
function getQuestions() : IQuestion[] {}
Type Guards
Type Guards
var x: serverAPI.giveMeXPlease();
if(typeof x === 'string') {
console.log(x.subtr(1)); // Error
}
// x is still any here
x.unknown(); // OK
instanceof
class Dog { woof() { } }
class Cat { meow() { } }
var pet = serverAPI.getPet();
if(pet instanceof Dog) {
pet.woof(); // OK
} else {
pet.woof(); // Error
}
User defined type guards
interface Animal {name: string; }
interface Cat extends Animal { meow(); }
function isCat(a: Animal): a is Cat {
return a.name === 'kitty';
}
var x: Animal;
if(isCat(x)) {
x.meow(); // OK, x is Cat in this block
}
Ambient Types
Example for ambient type
declare var angular : any;lib.d.ts a gift from TypeScript
-
17K lines of ambient declarations
eval, parseInt, encodeURI
Math, Date, RegExp
Full DOM declarations
And many more...
DefinitelyTyped a gift from the comunity
-
1458 contributors
-
15K commits
-
Thousands of definition files
-
node.d.ts
-
A word about tsd
Define declarations to existing js code
interface IMemberDetails {
name:string;
email:string;
id:string;
owner:boolean;
}
interface IWixStatic {
addEventListener(eventName:string, cb:Function):void;
closeWindow(message:Object):void;
currentMember(cb:(member:IMemberDetails)=>void):void;
}
declare var Wix : IWixStatic;
Extending a declaration*
declare module jasmine {
interface Matchers {
addMatchers(expected?: any): boolean;
toHaveBeenCalledOnce(): boolean;
toBeOneOf(expected?: any): boolean;
toHaveClass(expected?: any): boolean;
toBeDisabled(): boolean;
toMatchBiUrl(expected: any): boolean;
}
}
declare var questions : IQuestion[];
DTOs
&
Domain objects*
interface IOrderItemDTO {
title : string;
SKU : string;
option : string[];
price : number;
quantity : number;
total : number;
image : string;
}
interface IOrderDTO {
id : string;
items : IOrderItemDTO[];
createdDate : number;
quantity : number;
billingAddress : IAddressDTO;
shippingAddress : IAddressDTO;
buyerNote? : string;
currency : string;
}
Describe your DTOs as interfaces
class Order {
serialize(orderDTO:IOrderDTO) {
}
deserialize():IOrderDTO {
}
}
Domain objects
class Order implements IOrderDTO {
constructor(sourceOrderDTO:IOrderDTO) {
// copy to local variables
}
}
Domain objects (option #2)
interface IQuestionDTO {
askedTime : number;
askedBy: Attendee;
answers? : boolean;
}
Getters / Setters*
Under the hood
class SettingsData {
private m_padding : string;
get padding():string {
return this.m_padding;
}
set padding(value:string) {
this.m_padding = value;
}
}
var SettingsData = (function () {
function SettingsData() {
}
Object.defineProperty(SettingsData.prototype, "padding", {
get: function () {
return this.m_padding;
},
set: function (value) {
this.m_padding = value;
},
enumerable: true,
configurable: true
});
return SettingsData;
})();
Practical example
class SettingsData {
private m_padding : string;
private m_WixSDK : IWixSDK;
private sendToSdk(paramName:string, paramValue:any) {
this.m_WixSDK.setSettings(paramName, paramValue);
}
get padding():string {
return this.m_padding;
}
set padding(value:string) {
if (value !== this.m_padding) {
this.m_padding = value;
this.sendToSdk('padding', this.m_padding);
}
}
}
Practical example #2
class ReadOnlyData {
private m_id : number;
constructor() {
this.m_id = 0xF23256234;
}
get id():string {
return this.m_id ;
}
}
Enums*
Under the hood
enum LogType {None, Warning, Error, Fatal}
function log(message:string, logType:LogType) {
}
var LogType;
(function (LogType) {
LogType[LogType["None"] = 0] = "None";
LogType[LogType["Warning"] = 1] = "Warning";
LogType[LogType["Error"] = 2] = "Error";
LogType[LogType["Fatal"] = 3] = "Fatal";
})(LogType || (LogType = {}));
function log(message, logType) {
}
var LogType = {
0: "None",
1: "Warning",
2: "Error",
3: "Fatal",
"None": 0,
"Warning": 1,
"Error": 2,
"Fatal": 3
}
Control the id
enum LogType {None = 1000, Warning, Error, Fatal}
var logTypeStr = LogType[1000]; // "None"
var logTypeId : LogType = LogType["None"]; // 1000
string id
enum LogType {
None = <any>"LOG_TYPE_NONE",
Warning = <any>"LOG_TYPE_WARNING",
Error = <any>"LOG_TYPE_ERROR",
Fatal = <any>"LOG_TYPE_FATAL"
}
var LogType = {
"LOG_TYPE_NONE": "None",
"LOG_TYPE_WARNING": "Warning",
"LOG_TYPE_ERROR": "Error",
"LOG_TYPE_FATAL": "Fatal",
"None": "LOG_TYPE_NONE",
"Warning": "LOG_TYPE_WARNING",
"Error": "LOG_TYPE_ERROR",
"Fatal": "LOG_TYPE_FATAL"
}
Decorators*
Decorators are called when the class is declared
Multiple decorators can be defined
Decorators are not allowed on constructors
Don't use arrow function inside the decorator
Where can I decorate?
@MyClassDecorator
class Animal {
@MyPropertyDecorator
name : string;
@MyMethodDecorator
func1() {
}
func2(@MyParameterDecorator myParameter: string) {
}
}
Example: Log
function log(target, propertyKey, descriptor) {
// save a reference to the original method
var originalMethod = descriptor.value;
// override with our own implementation
descriptor.value = function(...args: any[]) {
console.log("The method args are: " + JSON.stringify(args));
var result = originalMethod.apply(this, args);
console.log("The return value is: " + result);
return result;
};
return descriptor;
}
class Foo {
@log
func1() {
}
}
Example: Deprecated
function deprecated(target, name, descriptor) {
var original = descriptor.value;
descriptor.value = function() {
console.warn(`Function ${name} is deprecated.`);
return original.apply(target, arguments);
};
return descriptor;
}
class Foo {
@deprecated()
func1() {
}
}
Example: WixSdk
class ProductWidgetStyleModel {
@sdkColor('nameColor', 0xFFFFFF)
nameColor;
@sdkFont('nameFont', 'Arial')
nameFont;
@sdkColor('dividerColor', 0xFF0000)
dividerColor;
@sdkUnit('dividerWidth', 20, 0, 245)
dividerWidth;
.
.
.
}
Things I didn't talk about
var questions;

Tools
grunt-ts
tsd
tslint
reference.ts
tsconfig.json*
Builders*
-
TBD:
The patterns
How we use it
Angular1.x
&
TypeScript
Dos and Don'ts
Avoid $scope
Controller as
Use classes
Component based structure
function MyDirective():angular.IDirective {
function link() {
// link implementation
}
return {
restrict: 'E',
replace: true,
templateUrl: '<h1>I am a directive</h1>',
controller: MyDirectiveController,
link: link
}
}
Directive
No inheritance
No this, in the link function
Factory
interface IProduct {
name : string;
id : string;
}
var deferred : ng.IDeferred<IProduct> = $q.defer<IProduct>();
var p : ng.IPromise<IProduct> = deferred.promise;
p.then((product:IProduct) => { console.log(product.name)});
Promises
interface IProduct {
name : string;
id : string;
}
var deferred = $q.defer();
var p = deferred.promise;
p.then((product:IProduct) => { console.log(product.name)});
interface IProduct {
name : string;
id : string;
}
var deferred = $q.defer<IProduct>();
var p = deferred.promise;
p.then((product) => { console.log(product.name)});
interface IProduct {
name : string;
id : string;
}
function loadProducts():ng.IPromise<IProduct[]> {
}
loadProducts.then((products) => { console.log(products)});
TypeScript
By gilamran
TypeScript
- 843