var x:number;
var y:string;
var z:number[];
var foo:{a:any;b:()=>any;} // powerful inline declaration
inline declarations
Offer the complete Interface syntax but inline. Syntax:
var __varname__: {
__membername__ : __membertype__ ;
__membername__ : __membertype__ ;
// Repeat
}
e.g:
var x:{
() : Function;
[foo:string]: number;
new() : string;
}
Function signature
// interface / inline interface implementation
interface fooInterface{
simpleSyntax():void;
lambdaSyntax:()=>void;
}
var fooInline:{
simpleSyntax():void;
lambdaSyntax:()=>void;
}
Lambda signature is required for function signatures in vars / parameter type declarations.
Function signature for callables
A bit inconsistent.
// Simple function, When declaring a var or function arguments
var x1 : (s: string)=>string;
// When saying something is callable
var x2 : { (s: string): string; }
interface x3{
(s:string):string;
}
// Errors:
var y1 : (s: string):string;
var y2: { (s: string)=> string; }
interface y3{
(s:string)=>string;
}
Type Inference
var x = 123;
var y;
y = 123;
x = "asdf";
y = "asdf";
var z={foo:123}
What type is y?
Does the compiler give an error in this code segment?
any
var x:any;
var y:number = x;
x = y;
Can be assigned to anything. Can be assigned anything.
null, undefined
Typescript: For compiler (not runtime) both are type any.
var x = null;
var y = undefined;
Javascript: null is an object instance to mean nothing. undefined is a type as well as a value for something not initialized.
var TestVar;
alert(TestVar); //shows undefined
alert(typeof TestVar); //shows undefined
var TestVar = null;
alert(TestVar); //shows null
alert(typeof TestVar); //shows object
Which string is that?
var obj:String = "123";
var primitive:string = "123";
obj=primitive; // exactly what the first line is doing
primitive=obj; // Error
Recommendation: Just use string.
Ambients
I thought I saw a pussy cat
Ambients
The magical keyword is ***declare***.
Generate no code whatsoever so obviously
- no variable initializer
- no function bodies / no default parameters
Ambients
declare var angular;
declare function foo(takes:string):number;
declare class Fancy{
notMuch:string;
}
??????
function foo(req:string,optional?:number){}
Default
Effectively optional. Can even do expressions
function foo( req:string, def:number=3, optional=req+def ) { }
Generates:
function foo(req, def, optional) {
if (typeof def === "undefined") { def = 3; }
if (typeof optional === "undefined") { optional = req + def; }
}
Rest
params denoted by ...
- explicitly type declared
- have to be last
function foo(req:string,...blabla:any[]){}
becomes
function foo(req) {
var blabla = [];
for (var _i = 0; _i < (arguments.length - 1); _i++) {
blabla[_i] = arguments[_i + 1];
}
}
Function overloading
interface IFoo{
test(x:string);
test(x:number);
}
Function overloading
The actual function should be able to accept any of the overload members.
class Foo implements IFoo {
test(x: string);
test(x: number);
test(x: any) {
if (typeof x === "string") {
//string code
} else {
//number code
}
}
}
The overloads go through the same code generation restrictions e.g you cannot define default values etc. Also the final signature is not directly callable from typescript since its generally an over generalized implementation (any etc).
Function overloading
How to handle variable parameters
function f():number; // Error
function f(x:any):any{
}
function g():number; // Okay
function g(x?:any):any{
}
Don't need to be in a class.
Function Instances
// global or module
function simpleSyntax():void{}
var lambdaSyntax1=():void=>{}
// class , basically remove function or var
class fooClass{
simpleSyntax():void{}
lambdaSyntax=():void=>{}
}
// object literal
var fooObject={
simpleSytax: function():void{},
lambdaSyntax: ():void=>{}
}
// Reminder the lambda based signature was a bit different:
// lambdaSyntax:()=>void;
Class
Now called class
Basic
class Foo{
public member:number;
static stat:number = 123;
constructor(){this.member = 123;}
func(){
this.member = 256;
}
}
Note: to access members you ALWAYS have to use this
Basic
var Foo = (function () {
function Foo() {
this.member = 123;
}
Foo.stat = 123;
Foo.prototype.func = function () {
this.member = 256;
};
return Foo;
})();
class Foo{
public member:number;
}
becomes:
var Foo = (function () {
function Foo() { }
return Foo;
})();
Interface implementation
Primarily for the person implementing the class. The class can be used in place of the interface even if it doesn't explictly implement that interface.
interface iFoo{
x:number;
}
class Foo1 implements iFoo{
x:number;
}
class Foo2{
x:number;
}
var la:iFoo;
la = new Foo1();
la = new Foo2();
Syntax Limitations
Not everthing that can be declared in an interface can be implemented by a Typescript class e.g. Indexible, call signatures
interface WidgetMap {
[name: string]: Widget;
}
var map: WidgetMap = {};
map['gear'] = new GearWidget();
var w = map['gear']; // w is inferred to type Widget
Syntax Limitations
Callable example
interface Foo {
(): any;
(value: any): void;
}
function createFoo(): Foo {
var getFunc = () => { console.log("get"); return "foo"; }
var setFunc = (value: any) => { console.log("set"); }
return (value?: any) => {
if (value) {
setFunc(value);
} else {
return getFunc();
}
}
}
var f = createFoo();
f("bar");
f();
Optional members?
interface foo{
x?:number;
}
class boo1 implements foo{
}
class boo2 implements foo{
x:number;
}
Class Inheritance
super : Not the Australian kind.
Basic
class FooBase{
}
class FooChild extends FooBase{
}
Basic
var __extends = this.__extends || function (d, b) {
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
var FooBase = (function () {
function FooBase() { }
return FooBase;
})();
var FooChild = (function (_super) {
__extends(FooChild, _super);
function FooChild() {
_super.apply(this, arguments);
}
return FooChild;
})(FooBase);
Static is Broken
var __extends = this.__extends || function (d, b) {
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
vs.
var __extends = this.__extends || function(d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function f() { this.constructor = d; }
f.prototype = b.prototype;
d.prototype = new f();
}
work item
super
class base {
test(foo:number){
console.log(foo);
}
}
class child extends base{
constructor(public x:number){
super();
super.test(x); // 15
this.test(x); // 25
}
test(foo:number){
console.log(foo+10);
}
}
var test=new child(15);
super
super called via call super (dot) goes directly to prototype
function child(x) {
_super.call(this);
this.x = x;
_super.prototype.test.call(this, x);
this.test(x);
}
Super Cautious
class A{
fooMem=10;
}
class B extends A{
constructor(){
console.log(this.fooMem); // undefined
super();
console.log(this.fooMem);
console.log(super.fooMem); // undefined
}
};
var test = new B();
Make super first call and only use for function access afterwords.
this
which one?
Save me
Typescript knows. But doesn't mean it will save you.
- Constructor / member functions
- *this* a type of containing class
- Static functions
- *this* is of type constructor function
- All other places its any
Err
Typescript will complain if you miss this. It will say member not defined. So novice way is to put in this.
class Example{
member = 10;
constructor(){
// Could have been an jquery call
// you were porting from js
setTimeout(function() {
alert(this.member);
},100);
}
}
var ex = new Example();
But this code is wrong since "this" is any and not the class instance.
Arrow Functions
// all of these are the same
var test1=(x:number)=>{return x*10};
var test2=(x:number)=>(x*10);
var test3=(x:number)=>x*10;
var test4=(x)=>x*10;
var test5=x=>x*10;
// {} require a return statement
// This is actually returning undefined
// which is a valid number and fails silently
var test6=(x:number)=>{x*10};
Arrow Functions
For lexical scoping.
var foo = () => {return this;}
generates:
var _this = this;
var foo = function () {
return _this;
};
Err Fix
class Example{
member = 10;
constructor(){
// Could have been an jquery call
// you were porting from js
setTimeout(()=>{
alert(this.member);
},100);
}
}
var ex = new Example();
So basically
Replace all functions in arguments with ()=>
Almost
Keep functions for:
- When you want this to be any. E.g. $.each.
- When you need both. Then resort to the manual closure trick of var self = this;
Pattern
When your class is just a collection of functions other people would call.
class Example{
func1:(number)=>void;
func2:()=>number;
member:number = 10;
constructor(){
this.func1=(x)=>{this.member=x};
this.func2=()=>this.member;
}
}
The body needs to seperate from declaration since this is only available in constructor / member functions.
Useful for libs like knockout.
Internal Module
Fetch boy!
Internal Modules
Like namespaces in C# but it is actually a singleton instance.
module M{
var s = "test"
export function f(){
return s;
}
}
Just the export keyword
Internal Modules
The variables inside the variables are only available to the current body of the module via closure.
// Generated
var M;
(function (M) {
var s = "test";
function f() {
return s;
}
M.f = f;
})(M || (M = {}));
I say current body since modules are also open ended and can therefore have more than one bodies.
Internal Modules
module M{
export var s = "test"
}
module M{
export var n = 123;
}
generates:
var M;
(function (M) {
M.s = "test";
})(M || (M = {}));
var M;
(function (M) {
M.n = 123;
})(M || (M = {}));
Quick nesting
module A.B.C{
}
// same as
module A{
export module B{
export module C{
}
}
}
And you can have this side by side as modules are open ended.
Aliasing
module x.y.z{
export var n =10;
}
// Alias
import foo = x.y.z;
console.log(foo.n);
generates:
var foo = x.y.z;
console.log(foo.n);
Careful with naming
module Test{
export class Test{
}
}
var Test;
(function (Test) {
var Test = (function () {
function Test() { }
return Test;
})();
Test.Test = Test;
})(Test || (Test = {}));
Tip: Do not reuse the module name inside the module
Declaration Spaces
- Types are contributed by interface , module , class.
- Class and module also contribute a variable (constructor for class , instance for module)
Declaration Spaces
"The name does not exist in the current scope".
module M
{
export interface P {}
}
import im = M;
var foo1:im.P; // Okay
var vm = M;
var foo2:vm.P; // Error
You will still get autocomplete.
Structural Typing
Perhaps I can give you something else?
Class have their own brand
interface iFoo{
x:number;
}
class Foo1 implements iFoo{
x:number;
}
class Foo2{
x:number;
}
var la:iFoo;
la = new Foo1();
la = new Foo2();
var fa:Foo1 = new Foo2(); //Error
That brand is too mainstream
Everything else modules, interfaces, inline declarations and inferred structures are behaviour free.
interface inter{
foo:number;
}
module mod{
export var foo:number;
}
var w:inter;
var x:mod;
var y:{foo:number;}
var z={foo:123}
w=x=y=z;
z=y=x=w;
Its okay to have more
interface inter{
foo:number;
}
var x:inter;
var y={
foo:123,
la:23
};
x = y;
y = x; //Error
Extending Built-ins
You get it, I get it, But does the compiler get it?
Built-in Interfaces
Present in lib.d.ts that ships with the typescript compiler.
interface Array {
toString(): string;
toLocaleString(): string;
concat(...items: _element[][]): _element[];
concat(...items: _element[]): _element[];
join(seperator?: string): string;
Extend them
Its just javascript
interface Array {
shuffle: () => any; // <-- Whatever signature you want.
}
Array.prototype.shuffle = function () { ... };
Prototype is an implicitly available static variable on any type and cannot be declared manually.
Type Assertion
Cause I said so!
As simple as it gets
// error
var x:HTMLCanvasElement = document.getElementById("canvasId");
// valid
var y:HTMLCanvasElement =
< HTMLCanvasElement > document.getElementById("canvasId");
// type inferred
var z =
< HTMLCanvasElement > document.getElementById("canvasId");
Should not be called type casting.
Interfaces FTW
1,2,3, Bottoms up!
JQuery 1
declare var $: any;
Any
JQuery 2
declare var $: JQueryStatic;
interface JQueryStatic {
// AJAX
ajax(...params:any[]);
Start with rest parameters.
Suppose you have javscript class with equivalent behaviour:
class Test{
static foo = 123;
bar = 456;
}
Simple solution
What the compiler will generate with --declaration flag:
declare class Test {
static foo: number;
public bar: number;
}
Not open ended!
Manual solution
Remember declaration spaces? Add one to to each space:
// Non static members
interface Test{
bar:number;
}
// Static members and constructors go here:
interface TestStatic {
new():Test;
foo:number;
}
declare var Test:TestStatic;
And its open ended.
Finally
One Final Note
This presentation uses TypeScript
This presentation uses TypeScript with Javascript (RequireJS + AngularJS) and encourage you to look at the source on github.