Typescript

Javascript makes sense

Usefull links during the talk:

https://github.com/FCalabria/js-to-ts-example.git

https://www.typescriptlang.org/play/index.html

Summary

  1. What is typing?
  2. Issues in ES2015 that typing solves
  3. Typescript basic types
  4. Some advanced types
  5. Other typescript features
  6. How to integrate typescript: a migration example

What is typing?

“A type system is a tractable syntactic method for proving the absence of certain program behaviors by classifying phrases according to the kinds of values they compute.”

Pierce, Benjamin C. (2002). Types and Programming Languages. MIT Press

In programming languages, a type system is a collection of rules that assign a property called type to various constructs a computer program consists of, such as variables, expressions, functions or modules.

https://en.wikipedia.org/wiki/Type_system

What is typing?

Javascript already has it: remember typeof operand.

The latest ECMAScript standard defines seven data types (6 of them are primitives):

Boolean

Number

String

Symbol

Null

Undefined

Object

What is typing?

So, what is typescript?

Typescript is a typed superset of Javascript that compiles to plain Javascript.

It allows static and dynamic type checking, and also implements new ES features in advance.

Issues in ES2015 that typing solves

Incorrect comparisons

var a = 1;
var b = '1';
console.log(a == b); // true

Issues in ES2015 that typing solves

Typos

var a = { myProperty: 'hello world' };
console.log(a.myPoperty); // undefined

Issues in ES2015 that typing solves

Passing incorrect values to methods

var a = '12-25-2016';
function printMonth(fullDate) {
    console.log(fullDate.getMonth());
}
printMonth(a); // Uncaught TypeError: fullDate.getMonth is not a function

Issues in ES2015 that typing solves

Even in tricky situations

var a = ['a', 'b', 'c'].push('d');
console.log(a.length); // undefined

Issues in ES2015 that typing solves

Or just being lazy for documentation

this.gnomeService.loadAllGnomes()
    .then(function(response) {
        console.log(response.data);
    });

Typescript basic types

A type is declared with the syntax variable: type

It can be done in every variable, even in function parameters or returns (this is specially useful!)

var a: number = 12;
var b: string;
function numberToString(num: number): string {
    return num.toString();
}
b = numberToString(a);

Typescript comes with the native javascript methods typed, and we can integrate typings for known libraries.

Typescript basic types

// Boolean
var a: boolean = false;

// Number
var b: number = 100;

// String
var c: string = 'something';

// Array
var d: string[] = ['array', 'of', 'strings'];
var e: Array<string> = ['two', 'ways', 'to', 'type'];

// Any
var f: any = 12;
f = f.toString();
f = [f];

Boolean

Number

String

Array

Any

Typescript basic types

// Tuple
var a: [string, number];
a = ["hello", 10]; // OK
a = [10, "hello"]; // Error

// Enum
enum Month {January = 1, February = 2};
var b: Month = Month.January;

enum Gender {Male, Female, Other, Unknown};
var c: Gender = Gender.Male; // 0
// NOTE: Ts@2.4 will allow string enums

// Void
function noReturn(): void {
    console.log('Im returning nothing');
};

Tuple

Void

Enum

Typescript basic types

// Never
function unreachableValue(): never {
    throw new Error('Out of the function');
}

// Null
var a: null = null;

// Undefined
var b: undefined = undefined;

Never

Undefined

Null

Typescript basic types

Interfaces

interface IUser {
    gender: Gender; // This is our custom enum!
    name: string;
    surname?: string; // Optional property. Use with care
    birthDate: Date; // Classes work as a type
    readonly mail: string; // We can declare read only properties
}

What is the point of all this if we can't declare our own "types"?

interface IManager extends IUser {
    canDelete: boolean;
}

Interfaces can be extended. This way we inherit properties

Also, classes can implement interfaces. Too complex for this talk. Sorry!

Some advanced types

Advanced types: how the hell do I...?

Type functions

function toNumber(value: number | string): number {
    return typeof value === 'number' ? value : parseInt(value);
}

Manage a param that can have multiple types (union types)

function doSomething(param: string, callback: (num: number) => any) {
    callback(parseInt(param));
}

doSomething('1', function (a: number) {}); // OK
doSomething('2', function (a: string) {}); // Error

Be careful with union types, they make the typing less reliable

Some advanced types

Advanced types: how the hell do I...?

Check the instance of a value

type Gender = 'male' | 'female' | 'other' | 'unknown';
var userGender: Gender = 'female';


function getSectionIcon(section: 'main' | 'aboutUs' | 'blog') {
    return 'assets/icons/' + section.toLowerCase() + '.gif';
}

getSectionIcon('aboutUs'); // 'assets/icons/aboutus.gif'
getSectionIcon('mainSection'); // Error argument of type 'mainSection'...

Restrict a string to some values (string literal types)

function addContent(whoIsAsking: IUser | IManager): void {
    if (whoIsAsking instanceof IUser) {
        throw new Error('You shall not pass!');
    } else {
        myApi.addContent();
    }
}

Some advanced types

Type inference, compatibility and assertion

Declaring types for everything can be a pain in the ass but luckily, typescript can guess types. That's call inference.

Even though it's a cool feature, if we are not careful it could led to inference "any" for everything. Don't be too lazy!

var a = [3]; // a is treated as number[]
a.push('2'); // Error

function transform(variable) {
    return variable.toString();
}
var b = transform(3); // b is treated as any

Some advanced types

Type inference, compatibility and assertion

Two different types or interfaces can be compatible.

interface Named {
    name: string;
}

var x: Named;
var y = { name: "Alice", location: "Seattle" };
x = y; // OK, because y has the only property that Named needs

enum MyValues { 'first', 'second', 'third' };
var a: MyValues;
var b: number = 0;
a = b; // OK, number is compatible with any enum

var c: number = 4;
a = 4; // Also OK, ANY number is compatible with ANY enum

Some advanced types

Type inference, compatibility and assertion

Sometimes, we don't want to add an interface for everything, or we just know more than Typescript. Here is where assertion comes in handy.

interface IFish {
    swim: () => void
}
interface IBird {
    fly: () => void
}
function isFish(pet: IFish | IBird): boolean {
    return (<IFish>pet).swim !== undefined;
    // Another syntax:
    // return (pet as IFish).swim !== undefined;
}

Other typescript features

With Typescript, we have access to oncoming ES features.

And, since it's later compiled to the javascript version of our choice, we don't need to care about browser compatibility.

// Typescript
const ROOT: string = 'localhost:8080/';
let filename: string = 'somefile.jpg';
let fullpath = `${ROOT}folder/${filename}`;
// ES5
var ROOT = 'localhost:8080/';
var filename = 'somefile.jpg';
var fullpath = ROOT + "folder/" + filename;

Early access to new ES features

Other typescript features

Early access to new ES features

// Typescript
[1, 2, 3, 4].filter((num: number) => num < 2);
// ES5
[1, 2, 3, 4].filter(function (num) { return num < 2; });

// Typescript
for (let val of ['a', 'b', 'c']) {
    console.log(val);
}
// ES5
for (var _i = 0, _a = ['a', 'b', 'c']; _i < _a.length; _i++) {
    var val = _a[_i];
    console.log(val);
}

Other typescript features

Early access to new ES features

// Typescript
[1, 2, 3, 4].filter((num: number) => num < 2);
// ES5
[1, 2, 3, 4].filter(function (num) { return num < 2; });

// Typescript
for (let val of ['a', 'b', 'c']) {
    console.log(val);
}
// ES5
for (var _i = 0, _a = ['a', 'b', 'c']; _i < _a.length; _i++) {
    var val = _a[_i];
    console.log(val);
}

How to integrate typescript

A migration example

First, download the repo and install the current dependancies

$ npm install --save-dev typescript awesome-typescript-loader

Then, add the main dependancies: typescript and a typescript webpack loader

$ git clone https://github.com/FCalabria/js-to-ts-example.git
$ npm install

How to integrate typescript

Initial configuration

Add a tsconfig.json file to the root of your project

{
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": false,
        "removeComments": true,
        "preserveConstEnums": true,
    }
}

https://www.typescriptlang.org/docs/handbook/compiler-options.html

How to integrate typescript

Initial configuration

Add the ts loaders in webpack.config.js, as new rules

rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      }, {
        test: /\.ts$/,
        loader: 'awesome-typescript-loader'
      }
    ]

How to integrate typescript

Initial configuration

Change the file extension to .ts on your javascript files, and in all references to them, and run webpack

$ npm start
ERROR in ./src/app.ts
Module not found: Error: Can't resolve './loading' in 'D:\js-to-ts-example\src'
 @ ./src/app.ts 2:0-32

How to integrate typescript

Initial configuration

Don't worry! When no extension is given, webpack asumes it's .js. There's two solutions:

require('./style.css');
var loading = require('./loading.ts'); // Add the extension
var axios = require('axios');
entry: './src/app.ts',
output: {
  filename: 'bundle.js',
  path: path.resolve(__dirname, 'dist')
},
resolve: {
  extensions: ['.ts', '.js'] // Configure webpack to try also with .ts files
},

How to integrate typescript

Initial configuration

We need types definitions to avoid some errors

$ npm install --save-dev @types/node
ERROR in [at-loader] ./src/app.ts:69:10
    TS2339: Property 'searchPhoto' does not exist on type 'Global'.

Nice, at least one error that makes sense. In this case we can use type assertion to solve it.

How to integrate typescript

Writing types

To start, change noImplicitAny to true and try to solve the errors

How to integrate typescript

Writing types

There's still errors, of course, but let's go to the next level: change tsconfig.json to add more checkings

{
    "compilerOptions": {
        "module": "commonjs",
        "strict": true,
        "noImplicitReturns": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "removeComments": true,
        "preserveConstEnums": true
    }
}

How to integrate typescript

Writing types

[at-loader] Using typescript@2.3.4 from typescript and "tsconfig.json" from D:\js-to-ts-example/tsconfig.json.


[at-loader] Checking started in a separate process...

[at-loader] Ok, 0.222 sec.

How to integrate typescript

Tslint

Tslint is a style checker for typescript, that adds more rules to our code, not only type checking.

 

It can be used with webpack adding tslint-loader to the proccess.

 

https://palantir.github.io/tslint/

Questions?

Made with Slides.com