Compiler

Compile Options Here

Conception

BasicTypes

  1. let isDone: boolean = false;
  2. let binary: number = 0b1010;
  3. let color: string = "blue";
  4. let list: number[] = [1, 2, 3];
  5. // use generic
  6. let list: Array<number> = [1, 2, 3];
  7. // use tupple type
  8. let x: [string, number];
  9. // use enum
  10. enum Color {Red = 1, Green = 2, Blue = 4};
  11. let c: Color = Color.Green;
  12. // any
  13. let notSure: any = 4;
  14. // void
  15. function warnUser(): void {
  16. alert("This is my warning message");
  17. }
  18. // null and undefined
  19. let u: undefined = undefined;
  20. let n: null = null;
  21. let strLength: number = (<string>someValue).length;
  22. let strLength: number = (someValue as string).length;
  23. let strLengths: Array<number> = (Array<number>values).length;
  24. type myDefinedType = string;
  25. type myCreatedType = string[];
  26. const num = <number> numberStringSwap('1234');
  27. const str = numberStringSwap(1234) as string;
  28. // intersection types
  29. interface Foo {
  30. name: string;
  31. count: number;
  32. }
  33. interface Bar {
  34. name: string;
  35. age: number;
  36. }
  37. export type FooBar = Foo & Bar;
  38. // Maped Types
  39. type ToPomise<T> = { [K in typeof T]: Promise<T[K]> };
  40. // Conditional types
  41. type InstanceOfExample = InstanceType<typeof Renderer>; // Renderer

Interface

interface SquareConfig {
    label: string;
    // ? stands for optional
    color?: string;
    width?: number;
    [propName: string]: any;
    (source: string, subString: string): boolean;
    cardType: RedCard | BlueCard | GreenCard;
    readonly x: number;
    readonly y: number;
    createCardPicker(this: Deck): () => Card;
}

let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
    let result = src.search(sub);
    return result > -1;
}

interface Shape { color: string; }
interface PenStroke { penWidth: number; }
interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

Advanced form:

interface DataConfig {
  [reportName: string]: [keyof PerformanceTiming, keyof PerformanceTiming]; // [startEvent, endEvent]
}

Class

class Person {
    protected name: string;
    protected constructor(theName: string) { this.name = theName; }
}

// Employee can extend Person
class Employee extends Person implements eat, shit, fuck {
      static origin = {x: 0, y: 0};
    static func = (a: string) => string;
    private department: string;
    protected protectedFn: () => any;
    readonly name: string;
    readonly numberOfLegs: number = 8;

    constructor(name: string, department: string) {
        super(name);
        this.department = department;
    }

    get fullName(): string {
        return this._fullName;
    }

    set fullName(newName: string) {
        if (passcode && passcode == "secret passcode") {
            this._fullName = newName;
        }
        else {
            console.log("Error: Unauthorized update of employee!");
        }
    }

    public getElevatorPitch() {
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}

let howard = new Employee("Howard", "Sales");
let john = new Person("John"); // Error: The 'Person' constructor is protected

Abstract Class

abstract class Department {

    constructor(public name: string) {}

    printName(): void {
        console.log("Department name: " + this.name);
    }

    abstract printMeeting(): void; // must be implemented in derived classes
}

class AccountingDepartment extends Department {
    constructor() {
        super("Accounting and Auditing"); // constructors in derived classes must call super()
    }
    printMeeting(): void {
        console.log("The Accounting Department meets each Monday at 10am.");
    }
    generateReports(): void {
        console.log("Generating accounting reports...");
    }
}

let department: Department; // ok to create a reference to an abstract type
department = new Department(); // error: cannot create an instance of an abstract class
department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
department.printName();
department.printMeeting();
department.generateReports(); // error: method doesn't exist on declared abstract type

Functions

// Function returning never must have unreachable end point
function error(message: string): never {
    throw new Error(message);
}

function buildName(firstName: string, lastName?: string) {
    if (lastName)
        return firstName + " " + lastName;
    else
        return firstName;
}

let result1 = buildName("Bob");                  // works correctly now
let result2 = buildName("Bob", "Adams", "Sr.");  // error, too many parameters
let result3 = buildName("Bob", "Adams");         // ah, just right


// reset parameters
function buildName(firstName: string, ...restOfName: string[]) {
    return firstName + " " + restOfName.join(" ");
}

interface Deck {
    suits: string[];
    cards: number[];
    createCardPicker(this: Deck): () => Card;
}

// a more complex one
export function onDidCreateEditor(listener: (codeEditor: ICodeEditor) => void)
    : IDisposable;
export function create(
    domElement: HTMLElement, 
    options?: IEditorConstructionOptions,
    override?: IEditorOverrideServices
): IStandaloneCodeEditor;
export function createModel(value: string, language?: string, uri?: Uri): IModel;

We can also describe function with over-loaded type:

declare function getWidget(n: number): Widget;
declare function getWidget(s: string): Widget[];

declare function fn(x: HTMLDivElement): string;
declare function fn(x: HTMLElement): number;
declare function fn(x: any): any;

var myElem: HTMLDivElement;
var x = fn(myElem); // x: string, :)

Generics

In languages like C# and Java, one of the main tools in the toolbox for creating reusable components is generics, that is, being able to create a component that can work over a variety of types rather than a single one. This allows users to consume these components and use their own types.

/**
 * Generics Functions
 */
function identity<T>(arg: T): T {
    return arg;
}

function loggingIdentity<T>(arg: Array<T>): Array<T> {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}

// or in this way
function loggingIdentity<T>(arg: T[]): T[] {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}

interface GenericIdentityFn<T> {
    (arg: T): T;
}

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: GenericIdentityFn<number> = identity;

/**
 * Generics Types
 */

interface Thenable<T> {
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled The callback to execute when the Promise is resolved.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of which ever callback is executed.
     */
    then<TResult>(
      onfulfilled?: (value: T) => TResult | Thenable<TResult>, 
      onrejected?: (reason: any) => TResult | Thenable<TResult>
    ): Thenable<TResult>;

    then<TResult>(
      onfulfilled?: (value: T) => TResult | Thenable<TResult>, 
      onrejected?: (reason: any) => void
    ) : Thenable<TResult>;
  }

interface IEvent<T> {
  (listener: (e: T) => any, thisArg?: any): IDisposable;
}

export class Emitter<T> {
  constructor();
  readonly event: IEvent<T>;
  fire(event?: T): void;
  dispose(): void;
}

getProperty

function getProperty<T, K extends keyof T>(o: T, name: K): T[K] {
    return o[name]; // o[name] is of type T[K]
}

Enmu

enum FileAccess {
    // constant members
    None,
    Read    = 1 << 1,
    Write   = 1 << 2,
    ReadWrite  = Read | Write,
    // computed member
    G = "123".length
}

export enum DocumentHighlightKind {
  /**
   * A textual occurrence.
   */
  Text = 0,
  /**
   * Read-access of a symbol, like reading a variable.
   */
  Read = 1,
  /**
   * Write-access of a symbol, like writing to a variable.
   */
  Write = 2,
}

Module

Normal Modules:

export * as Validator from "./StringValidator"; // exports interface 'StringValidator'

export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./ZipCodeValidator";

declare let $: JQuery;
export default $;

Module is an independent functional unit as Ambient Modules. To describe the shape of libraries not written in TypeScript, we need to declare the API that the library exposes.

We call declarations that don’t define an implementation “ambient”. Typically, these are defined in .d.ts files. If you’re familiar with C/C++, you can think of these as .h files. Let’s look at a few examples.

External Dependencies:

https://www.npmjs.com/~types

More to say, use /// <reference types="moment" /> to include a global libraries.

declare module "url" {
    export interface Url {
        protocol?: string;
        hostname?: string;
        pathname?: string;
    }

    export function parse(urlStr: string, parseQueryString?, slashesDenoteHost?): Url;
}

declare module "path" {
    export function normalize(p: string): string;
    export function join(...paths: any[]): string;
    export var sep: string;
}

/// <reference path="node.d.ts"/>
/// <reference path="../typings/global.d.ts" />
import * as URL from "url";
let myUrl = URL.parse("http://www.typescriptlang.org");

declare module 'visualEngine' {
  export interface engineVersion {
    version: string;
  }
}


declare module 'monaco.languages.typescript' {
  enum ScriptTarget {
    ES3 = 0,
    ES5 = 1,
    ES2015 = 2,
    ES2016 = 3,
    ES2017 = 4,
    ESNext = 5,
    Latest = 5,
  }

  export enum ModuleResolutionKind {
    Classic = 1,
    NodeJs = 2,
  }

  type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[];
  interface CompilerOptions {
    maxNodeModuleJsDepth?: number;
    module?: ModuleKind;
    moduleResolution?: ModuleResolutionKind;
    newLine?: NewLineKind;
    noEmit?: boolean;
    noEmitHelpers?: boolean;
    noEmitOnError?: boolean;
    noErrorTruncation?: boolean;
    noFallthroughCasesInSwitch?: boolean;
    noImplicitAny?: boolean;
    noImplicitReturns?: boolean;
    rootDirs?: string[];
    skipLibCheck?: boolean;
    skipDefaultLibCheck?: boolean;
    sourceMap?: boolean;
    sourceRoot?: string;
    strictNullChecks?: boolean;
    suppressExcessPropertyErrors?: boolean;
    suppressImplicitAnyIndexErrors?: boolean;
    target?: ScriptTarget;
    traceResolution?: boolean;
    types?: string[];
    /** Paths used to compute primary types search locations */
    typeRoots?: string[];
    [option: string]: CompilerOptionsValue | undefined;
  }

  export interface DiagnosticsOptions {
    noSemanticValidation?: boolean;
    noSyntaxValidation?: boolean;
  }

  export interface LanguageServiceDefaults {
    /**
     * Add an additional source file to the language service. Use this
     * for typescript (definition) files that won't be loaded as editor
     * document, like `jquery.d.ts`.
     *
     * @param content The file content
     * @param filePath An optional file path
     * @returns A disposabled which will remove the file from the
     * language service upon disposal.
     */
    addExtraLib(content: string, filePath?: string): IDisposable;

    /**
     * Set TypeScript compiler options.
     */
    setCompilerOptions(options: CompilerOptions): void;

    /**
     * Configure whether syntactic and/or semantic validation should
     * be performed
     */
    setDiagnosticsOptions(options: DiagnosticsOptions): void;

    /**
     * Configure when the worker shuts down. By default that is 2mins.
     *
     * @param value The maximun idle time in milliseconds. Values less than one
     * mean never shut down.
     */
    setMaximunWorkerIdleTime(value: number): void;
  }

  export var typescriptDefaults: LanguageServiceDefaults;
  export var javascriptDefaults: LanguageServiceDefaults;

  export var getTypeScriptWorker: () => monaco.Promise<any>;
  export var getJavaScriptWorker: () => monaco.Promise<any>;
}

Declarations

The typescript declarations template files demo.

  • Auto generating index.d.ts file dts-gen
  • Export instance
export interface Popups {
  init(): void;
}

declare const popups: Popups;
export default popups;
  • Export module in global
declare global {
  interface Window {
    dd: DingTalkGlobal;
  }
}

Realtime Practice

Make RE typescript :)

Why TypeScript?

  1. Problem you want to solve?
  2. Start to figure out how to solve that sort of problem?
    • monaco editor interface.
    • Angular 2 strongly support.
  3. Typescript ~

How to make it possible?

  1. Static type system like Java, ready for the large project. Module / Class / Function …
  2. Use interface to rule theme all, clean the mess, reveal the clean and beautiful design of your app. Everything is clear and ready.
  3. Code Intelligence just like Java but more powerful than Java itself with the core power of JavaScript.
  4. Purity for module / class / function design. Think and design before writing a byte of code.

Other features:

  1. Static type check / compiling bugs detect.
  2. Fake Java. Interfaces / Method modifier / Inheritance / Generalization / Enum (Use Trumps’ Image and PS it)
  3. Code refactoring and Test Friendly.

Quick Demo

  1. RE Code declarations. My mom shall never worry about the misunderstanding of the code that people write without attention :)
    • Not familiar with the whole architecture.
    • Not familiar with methods.
    • Not familiar with the data-structure passed through the code.
  2. Use reference of VE as ve.d.ts. Auto completion
  3. Design to make things great. HistoryPane API Design.

Summary

  1. Try Typescript and think in a different way.
  2. Use typescript for designing your App’s architecture. Draw a big picture of your complex system design just in a Typed System.
  3. Carefully design and craft those declarations shall be more friendly to a new developer. It is a good way to pay the debts of the future. To control the complexity in a more acceptable range.

Ref


Demo Project