Advanced Typescript
Overview
- Intersection and Union Types
- Type Guards, Polymorphic Distypes
- Decorators
- Async Patterns
- Linting
Advanced Basic Types
Destructing arrays and object
- Destructuring
- The process of assigning the elemenst of an arrrat or the props of and object to individual variables
- Rest Parameter
- Access the rest of an array in function params
let medals: string[] = ['gold', 'silver', 'bronze']; let [first, second, third] = medals;
function Log2Foo([foo1, foo2, ...rest]: Foo[]) { console.log(foo1); console.log(foo2); console.log(rest); }
function Log2Foo([foo1: one, foo2: two, ...rest]: Foo[]) { console.log(one); console.log(two); console.log(rest); }
let person = { name: 'fred', adddress: '1 main st', phone: 555-1234 }; let { name, address, phone } = person; console.log(name, address, phone);
let person = { name: 'fred', adddress: '1 main st', phone: 555-1234 }; let { name: personName, address: personAddress, phone: personPhone } = person; console.log(personName, personAddress, personPhone);
Spead operator
- Spead
- apply element into new elements in an array or function parameters
let items: number[] = [1, 2]; let extraitems: number[] = [10, 20]; items.push(...extraItems); console.log(items); // [1, 2, 10, 20]
let items: number[] = [1, 2]; let extraitems: number[] = [10, 20, ..items]; items.push(...extraItems); console.log(items); // [10, 20, 1, 2]
Tuple Types
- Tuple Type
- combinces a set of numerically named properties with the members of an array type
- Extension to arrays
- Inital Types defined and must match
- Additional element can be any defined type
#+NAME Creating and modifying Tuples
// 1. let idValue: [number, string] = [10, 'stringValue']; // 2. idValue[0] = 1; // OK idValue[0] = 'stringValue'; // Error idValue[1] = 'stringValue'; // OK idValue[1] = 1; // Error // 3. idValue[2] = 'stringValue'; // OK idValue[2] = 1; // OK
#+NAME KeyValuePair tuple type
interface KeyValuePair<KeyType, ValueType> extends Array<KeyType | ValueType> { 0: KeyType; 1: ValueType; }
Union Types
- Union Types
- Specifies several types for a value with
|
#+NAME Union Type in function parameter
function LogId(id: string | number) { console.log(id + " is either a string or number"); }
Intersection Type
- Intersection Types
- Specify a value that will contain all members of several types with
&
#+NAME Intersection Types as a return value
function FutureIsNow() : Clock & Radio { return new ClockRadio(); }
Mixins
- Mixin
- Classes whose functionality is added to another class
#+NAME Mixin
class Clock { void NovTwelve1955() {/*dostuff*/} } class Radio { void kbillysSuperSoundsOfTheSeventies() {/*dostuff*/} } class FutureDevice implements Clock, Radio { void NovTwelve1955() {/*empty*/} void kbillysSuperSoundsOfTheSeventies() {/*empty*/} } applyMixins(FutureDevice, [Clock, Radio]); function applyMixins(derivedCtor: any, baseCtors: any[]) { // function pulled straight from docs http://www.typescriptlang.org/docs/handbook/mixins.html baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { derivedCtor.prototype[name] = baseCtor.prototype[name]; }); }); }
String Literal Type
- String Literal Type
- String literal treated as a distinct type
#+NAME String Literal Type
let strLitType: 'Foo'; strLitType = 'Foo'; // OK strLitType = 'Baz'; // Error
#+NAME String Literal Type as Enum
let strLitType: 'Foo' | 'Bar'; strLitType = 'Foo'; // OK strLitType = 'Bar'; // OK strLitType = 'Baz'; // Error
Type Alias
- Type Alias
- refer to a type by a different name
#+NAME Type Alias for a String Literal Type
type FooBar = 'Foo' | 'Bar'; let foo: ForBar = 'Foo'; // OK let bar: ForBar = 'Bar'; // OK let baz: ForBar = 'Baz'; // Error
Advanced Types
Polymorphic this types
- Polymorphic this types
- A polymorphic this types represents a type that is the subtype of the containing class of interface
- used heavily in Fluent builders
#+NAME Polymorphic this type when class is extended
class Vehicle { Drive() => this; } class Truck extends Vehicle{}; class Car extends Vehicle{}; // different types returned Truck.Drive(); // returns typeof(Truck) Car.Drive(); // returns typeof(Car)
Declaration Merging
- Declaration Merging
- The complier merges two separate declarations decllared with the same name into a single definition
- allowed
- interfaces, enums, namspaces, namespaces with classes, namespaces with functions, namespaces with enums
- dissallowed
- classes with classes
#+NAME Model Merge by declaration merging
interface Power { powerSource: string; } interface Power { owner: string: } let foo: Power; foo.powerSource = 'shazam'; foo.owner = 'billy';
#+NAME Module Augentation by declaration merging
// classes.cs inteface IFoo { bar(); } export class Foo extends IFoo { bar() {} } // FooExtension.cs import { Foo } from './classes'; declare module './classes' { interface IFoo { baz(): void; } } Foo.prototype.baz = function() { // new method added to Foo; }
Type Guards
- typeof
- Check string | number | boolean | symbol
#+NAME typeof Type Guard
let x: string | number = 11; if (typeof x === 'string') { // string type enforced }
- instanceof
- check union types
#+NAME instance of Type Guard
let device: Clock | Radio; if (device instanceof Clock) { // Clock type enforced }
- type predicate
- check interfaces
#+NAME type predicate Type Guard
interface Foo { bar: string } function isFoo(x: any): x is Foo { return (<Foo>x).bar !== undefined; }
Symbols
- Requires
tsconfig.js::{compilerOptions{target: 'es2015'}}
- Characteristics
- Primitive Data Type
- Unique
- Immutable
- Use Cases
- Unique Constraints
- Computed Property Declarations
- Customize Internal Language Behaviour, there are a few well know symbols (hasInstance…)
#+NAME Symbol Initalization
let foo = Symbol('description'); let bar = Symbol('description'); console.log(foo === bar); // false console.log(typeof foo); // 'symbol'
#+NAME Symbol as property key
let fooSymbol = Symbol('description'); let bazObject = { [foo]: 'value for my symbol key' } console.log(bazObject[fooSymbol]); // Value for my symbol key
#+NAME Symbol for a Computed Property Key
// classes.ts let FOO_SYMBOL = Symbol(); export class BazClass { [FOO_SYMBOL]() { console.log('Some Message'); } } // usage.cs import {FOO_SYMBOL, BazClass} from './classes.ts'; let baz = new BazClass(); baz[FOO_SYMBOL](); // 'Some message'
#+NAME Symbol to modify internal manguage behaviours by overriding Symbol.hasInstance
// classes.tse export class BazClass { static [Symbol.hasInstance](obj: Object) { // do different check } }
Decorators
Class Decorators
#+NAME Class Decorator
function classDecorator(classCtor: Function) { /*do stuff*/ } @classDecorator class FooClass { barMethod(){} }
#+NAME Replace a constructor with a Class Decorator
function classDecorator<TFunction extends Function>(classCtor: TFunction): TFunction { let newCtor: Function = function() { console.log('Live from new ctor...'); } newCtor.prototype = Object.create(classCtor.prototype); newCtor.prototype.constructor = classCtor; return <TFunction>newCtor; }
Decorator Factory
- Decorator Factories
- Allow specifying additonal parameters
#+NAME Class Decorator Factory
function classDecoratorFactory(element:string) { return function(classCtor: Function): void { console.log(element); } } @classDecoratorFactory('elementValue') class FooClass {}
Property Decorators
#+NAME Property Decorator
function propertyDecorator(classCtorOrPrototype: Function, propertyKey: string) { /*do stuff*/ } class FooClass { @propertyDecorator barProperty: string; }
Parameter Decorators
#+NAME Parameter Decorator
function propertyDecorator(classCtorOrPrototype: Function, propertyKey: string, parameterIndex: number) { /*do stuff*/ } class FooClass { function barMethod(@parameterDecorator baz:string){} }
Metho
Method and Accessor Decorators
#+NAME Method Decorator
function methodDecorator(staticMethodOrPrototypeMethod: any, nameOfDecoratedMember: string, description: ProperyDescriptor) { /*do stuff*/ } class FooClass { @methodDecorator barMethod(){} }
- Property Descriptors
- Object that describes a property and how it can be manipulated
#+NAME PropertyDescriptor interface
interface PropertyDescriptor { configurable?: boolean; enumerable?: boolean; value?: any; // the value assigned (eg, the function for class methods) writable?: boolean; // is it readonly? get? (): any; set?(v: any); }
#+NAME creating a readonly Method with Method Decorator and PropertyDescriptor
function readonly(target: any, propertyKey: string, descriptor: ProperyDescriptor) { console.log(`Setting ${propertyKey} to readonly.`); descriptor.writable = false; } class FooClass { @readonly barMethod(){} }
Asyncronous Methods
Callbacks
#+NAME Callback Flow
interface FooCallback (err: Error, data: string[]): void; function FooMakesCallback(callback: FooCallback) { let data = fetchData(); if (data.length > 0) { callback(null, data); } else { callback(()=>{console.log('there was an error')}, null); } }
Promises
#+NAME Promises flow
let p: Promise<TypeOfSuccess> = new Promise((resolve, reject) => { // ...perform async work if (success) { resolve(data); } else{ reject(reason); } }); p.then(successResult => console.log('success' + successResult); p.catch(reason => console.log('rejected' + reason);
Async / Await
#+NAME Async/Await Flow
async function doAsyncWork() { let result = await SlowFunction(); console.log(result); }
#+NAME Async/Await Error handling using the promise returned
async function doAsyncWork() { let result = await SlowFunction(); console.log(result); } doAsyncWork.catch(reason => console.log(reason));