Typescript on Node.js

Taming Javascript with typing







Presentation created by Peter Snider

GitHub LinkedIn Yahoo

The Problems w JavaScript

(a.k.a. Features that lead to Problems)

  • No compiler -- syntax is checked at run time.
  • Dynamic variable creation with no error checking mechanism.
  • Implied global variables.
  • Every misspelled variable name is accepted for writing.
  • No scope checking.
  • Weak typing.
  • Late binding.
  • No modules.
  • Arcane rules for comparison and conversion.

The Solutions

  • JSLint
  • Coffescript
  • Google Closure Compiler Annotations
  • TypeScript
  • ... plus many others

Pros

  • Works on JavaScript.
  • Finds many problems.

Cons

  • Type-less systems cannot infer intent.
  • Misses many types of errors.

Pros

  • Adds many convenient features from Ruby and Python.
  • Can be read directly by Node.js, without compilation.

Cons

  • Requires learning a new language.
  • Doesn't address the problem of misspelled variable names.
  • Doesn't address weak typing.

Example


for filename in list
  do (filename) ->
    fs.readFile filename, (err, contents) ->
      compile filename, contents.toString()

Pros

  • Uses plain javascript, so no compilation is required.
  • Helps manage large projects.

Cons

  • Uses JavaDoc-style annotations to specify types.
  • Rules for code optimization are complex.

Example


/**
 * @param {!Array.<T>|!$jscomp.Iterable.<T>} iterable
 * @return {!$jscomp.Iterator.<T>}
 * @template T
 */
$jscomp.makeIterator = function(iterable) {
							

Pros
  • TypeScript is a superset of Javascript.
  • Adds type annotation to the code.
  • Strong module and class support.
  • Great for managing large projects.
Cons
  • Must be compiled.
  • Can be complex.
Example

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
							

100+ Others

Just because it can be done, doesn't mean it should!

The author of CoffeeScript has a list of languages that compile into javascript.

TypeScript on Node.js

  • External Modules for all code. (commonjs)
  • keywords:   declare module, import, export, require
  • Type Declarations
  • keywords and operators:   any, void, : TYPE, ?, () => TYPE
  • Interfaces for JSON-compatible types.
  • keywords:   interface, extends
  • Classes for complex types.
  • keywords:   class, implements, public, private, static
  • Type Declaration files for all 3rd-party code.
  • keywords:   reference
  • ES6 Arrow Functions
  • operators:   () => {}

External Modules for all code. (commonjs or amd)

keywords:   declare module, import, export, require
tsc --module commonjs --outDir generated src/ConfigService.ts
decl/ConfigService.d.ts

/// <reference path='../decl/ConfigCommon.d.ts' />
declare module 'ConfigService' {
    export var SAVED_STATE : ISavedState;
    export function get(key : string) : any;
}
							
src/ConfigService.ts

/// <reference path='../decl/import/node.d.ts' />
/// <reference path='../decl/import/Q.d.ts' />
/// <reference path='../decl/ConfigCommon.d.ts' />
import Q                     = require('q')
import common                = require('ConfigCommon');

var CONFIG : IConfig = <IConfig>{};
export var SAVED_STATE : ISavedState = <ISavedState>{};
export function get(key : string) : any {
	// body omitted
}
							

Type Declarations

keywords and operators:   : TYPE, ?, any, void, <TYPE>, () => TYPE

TypeScript infers types when they aren't specified.


/// <reference path='decl/import/node.d.ts' />
import fs = require('fs');
var obj : any;
var inferred_later;
var str : string;
var str_inferred = 'hello';
var SAVED_STATS : fs.Stats = <fs.Stats>{};
function greet(who? : string) : void {
	if (who == null)  who = 'everybody';
	console.log('Hello ' + who + '!');
}
var current_greeter : (who? : string) => void = greet;
current_greeter()
current_greeter('guests')
							

Interfaces for JSON-compatible types.

keywords:   interface, extends

interface IPersonName {
    family?: string;
    given?: string;
}
interface IPerson {
    _id?:               string;   // DatabaseObjectID
    account_email?:     string;
    name?:              IPersonName;
}
interface IBaker extends IPerson {
    breads:             string[];
    pastries:           string[];
}
						

Classes for complex types.

keywords:   class, implements, public, private, static

/// <reference path='decl/import/google.maps.d.ts' />
import maps = require('google.maps');
class Person implements IPerson {
    _id : string;
    public account_email : string;
    name : IPersonName;
    private location : maps.LatLng;
	
    constructor(person : IPerson);
    static getFullName(person : IPerson) : string;
    setLocation(location : maps.LatLng) : void;
    getLocation() : maps.LatLng;
}
						

Type Declaration files for all 3rd-party code.

keywords:   reference

Most popular javascript libraries already have TypeScript declarations.

Most are hosted on DefinitelyTyped. For example:

You will also write your own declaration files, one for each module in your application.

ES6 Arrow Functions

operators:   () => {}

These provide a clean way to deal with this in a different context.


/// <reference path='decl/import/q.d.ts' />
import Q = require('q');
function test_promise() {
    var promise : Q.IPromise<number> = Q.fcall(function() {
        if ((Date.now() % 3) == 0)  throw new Error('Oh NO! didnt expect time divisible by three!');
        return 10;
    });
    promise.then(
        (result) => {
            this.a = result;
        },
        (error) => {
            this.a = 'oopsie-daisy';
        }
    );
    return promise;
}
var o = {a: 0};
test_promise.call(o).finally(function() {console.log('o.a=' + o.a);});
						

Guidelines

  • Guidelines for Code Organization
  • Guidelines for Style
  • Guidelines that I'm Missing!

Guidelines for Code Organization

  • Use external modules (and never internal).
  • Every external module must have a like-named decl file.
  • Prefer smaller modules over larger ones.
  • Modules should export only the minimum required.
  • Every module should compile standalone.
  • Every module should have tests for its public interfaces.

Guidelines for Style

  • Marshalled classes should have corresponding interfaces.
  • Avoid classes until they become necessary.
  • It keeps the code lighter.
  • Keep an application vocabulary dictionary.
  • This may help you manage the symbol names in the app.
  • Use import when appropriate, and not var.

Guidelines that I'm Missing!

  • When should module names include a dot '.' or a slash '/'?
  • When should modules be combined?

Summary

    TypeScript is:

  • Great for managing large projects.
  • Has an immediate and incremental migration path.
  • Has modules and classes.
  • Tracks ES6 and ES7.
  • Open source on github.
  • Created and supported by Microsoft.
  • It's at version 1.0, and is stable and reliable.

Problems Solved:

  • Strong type checking at compile time.
  • This removes the need for a large category of unit tests.
  • Types can be declared to match any existing JavaScript.
  • TypeScript mixes freely with JavaScript
  • ...mixing the best of compile time type checking with the best of dynamic typing
  • Provides strong module support.
  • Catches most misspelled symbol names when types are used.
  • Prevents polluting the global namespace.

Problems Created:

  • Some of the details of TypeScript are complex.
  • So it takes a long time to learn the language well. I had trouble with:
    • module management
    • ...especially internal vs. external
    • type declarations for esoteric types
    • writing type declaration files
  • Management of 3rd-party type declaration files is ad-hoc.
  • But I just learned about tsd. Perhaps it will help.
  • Type declaration files don't have a versioning scheme yet.
  • So it's hard to determine whether you have the correct version of a type declaration file.

Problems Created that aren't Actually Problems:

  • Requires compilation.
  • Really! You want this!
  • Requires type declaration files separate from the source.
  • When a package and its type declarations file are managed separately, the files must be separate.
  • Correcting type errors can be tedious.
  • But it beats the alternative!

Resources

  • Tutorials and Study Guides
  • References and Tools
  • Help

Tutorials and Study Guides

    I recommend you review these in the order given.

  • A video overview, by the creator of TypeScript.
  • Quick Start Guide
  • A very brief overview of some of the most common features.
  • An interactive TypeScript compiler in your browser.
  • I suggest that you run all of your study examples in this tool, as this is the quickest way to learn what the compiler generates.
    Unfortunately, it doesn't support external modules.
  • The TypeScript Handbook
  • A detailed but short description of the language.
  • Various sample code.
  • I read some of these samples, but didn't build and run them.

References and Tools

    Use these as you need:

  • The DefinitelyTyped repository of type declaration files.
  • You need this.
    For even the smallest project, you will start with node.d.ts.
  • The TypeScript Language Specification
  • This is a bear. But it's also necessary reading once you hit problems that need detailed explanation.

Help