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));