Decorators are called at run-time, and can modify whatever they are decorating.
Kito Mann (@kito99), Virtua, Inc.
Principal Consultant at Virtua (http://virtua.tech)
Training, consulting, architecture, mentoring
JSF, Java EE, Polymer/Web Components, Angular
Official US PrimeFaces and PrimeNG partner
Author, JavaServer Faces in Action
Founder, JSF Central (http://www.jsfcentral.com)
Co-host, Enterprise Java Newscast (http://enterprisejavanews.com)
Java Champion
Google Developer Expert in Web Technologies
Internationally recognized speaker
JavaOne, JavaZone, Devoxx, Devnexus, NFJS, etc.
JCP Member
JSF, MVC, JSF Portlet Bridge, Portlets
Worked on a couple of TS projects
New to TypeScript
Leading a team on a TypeScript project
JavaScript is pretty cool
…but it is somewhat flawed
TypeScript is "JavaScript that scales"
Open source (started and maintained by Microsoft)
Lead architect is Anders Hejlsberg (C#, Delphi, Turbo Pasal)
Support for the ES5, ES6, and ES.next features
Transpiler like Babel, Traceur, or Clojure
An extensive type system built to work within the world of JavaScript
Allows code completion (IntelliSense), refactoring, and other features
Reduces errors
Effective use of TypeScript requires understanding both the newer JavaScript features and the TypeScript type system
Class syntax
class Shape {
constructor(id, x, y) {
this.id = id;
this.x = x;
this.y = y;
}
}
class Rectangle extends Shape {
constructor (id, x, y, width, height) {
super(id, x, y)
this.width = width
this.height = height
}
}
class Circle extends Shape {
constructor (id, x, y, radius) {
super(id, x, y)
this.radius = radius
}
}
let
and const
let a = [5,4,"foo",45];
for (let i = 0; i < a.length; i++) {
let x = a[i];
}
console.log(a); // [5,4,"foo",45]
console.log(i); // undefined
console.log(x); // undefined
const immutable = 45;
immutable = 46; // error
Arrow functions
this.nums.forEach((v) => {
if (v % 5 === 0)
this.fives.push(v)
})
Promises
function msgAfterTimeout (msg, who, timeout) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(`${msg} Hello ${who}!`), timeout)
})
}
msgAfterTimeout("", "Foo", 100).then((msg) =>
msgAfterTimeout(msg, "Bar", 200)
).then((msg) => {
console.log(`done after 300ms:${msg}`)
})
Symbols
let f = Symbol("foo");
let f2 = Symbol("foo");
f === f2; // false
let f3 = f;
f === f3; // true
Template literals
let customer = { name: "Foo" }
let card = { amount: 7, product: "Bar", unitprice: 42 }
let message = `Hello ${customer.name}, want to buy ${card.amount} ${card.product} for a total of ${card.amount * card.unitprice} bucks?`
console.log(message)
// Output: Hello Foo, want to buy 7 Bar for a total of 294 bucks?
Destructuring
let { op, lhs, rhs } = getASTNode()
let tmp = getASTNode();
let op = tmp.op;
let lhs = tmp.lhs;
let rhs = tmp.rhs;
Spread operator
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));
// Output: 6
Spread operator
const numbers = [1, 2, 3];
const numbersClone = [...numbers]; // same as newNumbers.slice()
const numbersAndStrings = ['a', 'b', 'c', ...newNumbers]; // ['a', 'b', 'c', 1, 2, 3]
const widget = { weight: 50, length: 100, width: 100, color: black }
const clone = {...widget} // shallow copy; similar to Object.assign()
widget === clone; // false
Rest parameters
function log(sender, functionName, ...additionalInfo) {
console.log("[LOG]","sender:", sender, "function:", functionName, "Additional info:", additionalInfo);
}
log("MyObj", "myFunc", "a", "b", "c");
// Output: [LOG] sender MyObj function myFunc Additional info (3) ["a", "b", "c"]
Decorators
@component('prop-app')
export class App extends NowElements.BasicApp {
/** Primary route object. */
@property({
type: Object,
notify: true
})
route: any;
/** Subroute (tail); for consumption by child pages. */
@property({
type: Object,
notify: true
})
subroute: any;
@property({type: Object})
routeData: any;
@property({type: String})
reqNum: string;
...
@observe('reqNum')
_loadRequisition(reqNum: string) {
console.debug(this.is, '_loadRequisition', reqNum);
if (reqNum && reqNum) {
this._addType = AppType.Requisition;
this.$.getReqInfo.params = {filter: "ReqNum=" + reqNum};
this.$.getReqInfo.generateRequest();
}
}
@listen(BasicAppLayout.SEARCH_EXECUTED_EVENT)
private _search(evt: PolymerEvent) {
let query = evt.detail;
if (this._addType === AppType.Project) {
if (!this.queryParams) {
this.queryParams = new QueryParams();
}
this.set('queryParams.projectQuery', query);
this.navigateToRoute('/project-list');
} else {
this.set('queryParams.reqQuery', query);
this.navigateToRoute('/requisition-list');
}
}
...
}
Decorators
// @component class decorator
function component(tagname, extendsTag) {
return function (target) {
target.prototype["is"] = tagname;
if (extendsTag !== undefined) {
target.prototype["extends"] = extendsTag;
}
}
}
Decorators are called at run-time, and can modify whatever they are decorating.
Decorators
// @listen method decorator
function listen(eventName) {
return (target, propertyKey, descriptor) => {
target.listeners = target.listeners || {};
target.listeners[eventName] = propertyKey;
}
}
TypeScript also supports accessor, property, and parameter decorators
Modules
Async / await
Generators and Iterators
Maps and Sets
Proxying and Reflection
Internationalization / Localization
get name() {
return this._name
}
set name(name) {
this._name = name;
}
get showPanel() {
return this._name && this.active && this.loadComplete;
}
Falsy
if (false)
if (null)
if (undefined)
if (0)
if (NaN)
if ('')
if ("")
if (``)
Every thing else is truthy
Outputs JavaScript
Can target ES3-ES.next
Type system does not affect runtime
TypeScript playground https://www.typescriptlang.org/play/index.html
Primitives
null
, undefined
, number
, string
, boolean
, symbol
, BigInt
(es.next)
Objects
Includes Function
and Array
A variable can be re-assigned to any primtive or object
let a = 1;
a = 'foo';
a = { color: 'red', size: 12 };
Every variable has a type (which may be inferred)
Rules govern which types may be assigned to each other
Fully supports generics
null
and undefined
let name: string; // default is undefined
name = "Kito";
name = null; // OK
name = undefined; // OK
// with --strictNullChecks
let name = "Kito"; // must be initialized
name = null; // Error
name = undefined; // Error
any
let foo: any; // same as "let foo;"
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)
let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
let list: any[] = [1, true, "free"];
list[1] = 100;
void
function log(sender: any, functionName: string,
...additionalInfo: any[]): void {
console.log("[LOG]","sender:", sender,
"function:", functionName, "Additional info:", additionalInfo);
}
never
// Function returning never must have unreachable end point
function error(message: string): never {
throw new Error(message);
}
// Inferred return type is never
function fail() {
return error("Something failed");
}
// Function returning never must have unreachable end point
function infiniteLoop(): never {
while (true) {
}
}
unknown
Type-safe version of any
let x: unknown = 5;
x == 5; // true
x !== 10; // true
x >= 0; // Error
x + 1; // Error
x * 2; // Error
-x; // Error
+x; // Error
x.foo(); // Error
x as number * 2; // 10
declare function isFunction(x: unknown): x is Function;
function f20(x: unknown) {
if (typeof x === "string" || typeof x === "number") {
x; // string | number
}
if (x instanceof Error) {
x; // Error
}
if (isFunction(x)) {
x; // Function
}
}
enum
enum Direction {
Back,
Forwards,
Left,
Right = 10
}
console.log(Direction.Back); // 0
console.log(Direction.Right); // 10
console.log(0 === Direction.Back); // true
class Walker {
walk(dir: Direction) {
switch (dir) {
case Direction.Back: console.log('Walk Back'); break;
case Direction.Forwards: console.log('Walk Forwards'); break;
case Direction.Left: console.log('Walk Left'); break;
case Direction.Right: console.log('Walk Right'); break;
}
}
}
const joe = new Walker();
joe.walk(Direction.Left); // "Walk Left"
joe.walk(Direction.Right); // "Walk Right"
// same result as switch stattement
class Walker {
walk(dir: Direction) {
console.log(`Walk ${Direction[dir]}`);
}
}
enum
enum Messages {
Confirmation = 'Are you really sure you want to do that?',
Error = 'Uh oh. Something bad happened...',
Welcome = 'Welcome to our wonderful app.'
}
console.log(Messages.Confirmation); // Are you really sure you want to do that?
console.log('Welcome to our wonderful app.' === Messages.Welcome); // true
tuple
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ["hello", 10]; // OK
// Initialize it incorrectly
x = [10, "hello"]; // Error
// When accessing an element with a known index, the correct type is retrieved:
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
// When accessing an element outside the set of known indices, a union type is used instead:
x[3] = "world"; // OK, 'string' can be assigned to 'string | number'
console.log(x[5].toString()); // OK, 'string' and 'number' both have 'toString'
x[6] = true; // Error, 'boolean' isn't 'string | number'
intersection
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
class Person {
constructor(public name: string) { }
}
interface Loggable {
log(): void;
}
class ConsoleLogger implements Loggable {
log() {
// ...
}
}
var jim = extend(new Person("Jim"), new ConsoleLogger());
var n = jim.name;
jim.log();
union
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
let indentedString = padLeft("Hello there!", ' ');
indentedString = padLeft("Hello there!", 000000);
indentedString = padLeft("Hello world", true); // error
union
class FancyPanel {
style: 'accordion' | 'collapsible' | 'normal';
init() {
if (this.style === 'accordion') {
...
} else
if (this.style === 'collapsible') {
...
} else
if (this.style === 'normll') { // error
...
}
}
}
index
interface Person {
name: string;
age: number;
}
let person: Person = {
name: 'Bill Gates',
age: 63
};
let personKeys: keyof Person; // name | age
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
let strings: string[] = pluck(person, ['name']); // ok, string[]
Structural type system
All TypeScript types are transpiled to JavaScript types at runtime
Considered "unsound"
Basic rule: if x
is compatible with y
if x
has at least the same members as x
interface Weighed {
weight: number;
}
let x: Weighed;
// y's inferred type is { name: string; weight: number; }
let y = { name: "Mickey", weight: 400 };
x = y;
y = x; // error
class Widget {
name: string;
weight: number;
color: string;
}
let w: Widget;
x = w;
w = x; // error
function widgetPackager(widget: Widget) { };
widgetPackager(w);
widgetPackager({ name: "Mouse", weight: 500, color: "red"});
widgetPackager(x); // Error
Using types isn’t all that useful if you don’t use them for external libraries
TypeScript provides types for non-TypeScript libraries through declaration (d.ts
) files
Ships with types for libraries for the output target
ES3, ES5, ES6, etc.
Many libraries include them, but you can install them if they don’t
npm install --save @types/lodash
import * as _ from "lodash";
_.padStart("Hello TypeScript!", 20, " ");
TypeScript declaration file search: https://aka.ms/types
If there are no existing libraries, declare
it:
declare const myLibrary: any;
myLibrary.doSomething();
Can be specified in tsconfig.json
file (preferred) or command line
Recommended options:
--experimentalDecorators
--target ("ES3" (default), "ES5", "ES6"/"ES2015", "ES2016", "ES2017" or "ESNext")
--noEmitOnError
--strict enables:
--noImplicitAny
--noImplicitThis
--alwaysStrict
--strictBindCallApply
--strictNullChecks
--strictFunctionTypes
--strictPropertyInitialization
Understnding new JavaScript features
Using TypeScript’s type system and other language features
Setting up your project to be as strict as possible
TypeScript docs: https://www.typescriptlang.org/docs/
TypeScript deep dive: https://basarat.gitbooks.io/typescript/
Twitter: @kito99
Company: http://virtua.tech