Oliver Zeigermann / @DJCordhose
Slides: http://bit.ly/ms-typescript
type annotations
Anders Hejlsberg@Build2016: Big JavaScript codebases tend to become "read-only".
let obj: string;
obj = 'yo';
// Error: Type 'number' is not assignable to type 'string'.
obj = 10;
function sayIt(what: string) { // types can be inferred (return type)
return `Saying: ${what}`;
}
const said: string = sayIt(obj);
class Sayer {
what: string; // mandatory
constructor(what: string) {
this.what = what;
}
sayIt(): string { // return type if you want to
return `Saying: ${this.what}`;
}
}
Flow looks the same for those examples
function foo(num: number) {
if (num > 10) {
return 'cool';
}
}
console.log(foo(9).toString());
// error: call of method `toString`.
// Method cannot be called on possibly null value
console.log(foo(9).toString());
// to fix this, we need to check the result
const fooed: string|void = foo(9);
if (fooed) {
fooed.toString();
}
Types are non-nullable by default in flow
function foo(num: number) {
if (num > 10) {
return 'cool';
}
}
// same as flow
const fooed: string|void = foo(9);
if (fooed) {
fooed.toString();
}
// or tell the compiler we know better (in this case we actually do)
fooed!.toString();
Only applies to TypeScript 2.x
Only works when strictNullChecks option is checked
All types nullable by default in TypeScript 1.x
can be anything, not specified
can selectively disable type checking
function func(a: any) {
return a + 5;
}
// cool
let r1: string = func(10);
// cool
let r2: boolean = func('wat');
Types can be parameterized by others
Most common with collection types
let cats: Array<Cat> = []; // can only contain cats
let animals: Array<Animal> = []; // can only contain animals
// nope, no cat
cats.push(10);
// nope, no cat
cats.push(new Animal('Fido'));
// cool, is a cat
cats.push(new Cat('Purry'));
// cool, cat is a sub type of animal
animals.push(new Cat('Purry'));
let cats: Array<Cat> = []; // can only contain cats
let animals: Array<Animal> = []; // can only contain animals
// error TS2322: Type 'Animal[]' is not assignable to type 'Cat[]'.
// Type 'Animal' is not assignable to type 'Cat'.
// Property 'purrFactor' is missing in type 'Animal'.
cats = animals;
// wow, works, but is no longer safe
animals = cats;
// because those are now all cool
animals.push(new Dog('Brutus'));
animals.push(new Animal('Twinky'));
// ouch:
cats.forEach(cat => console.log(`Cat: ${cat.name}`));
// Cat: Purry
// Cat: Brutus
// Cat: Twinky
TypeScript allows for birds and dogs to be cats here :)
let cats: Array<Cat> = []; // can only contain cats
let animals: Array<Animal> = []; // can only contain animals
// ERROR
// property `purrFactor` of Cat. Property not found in Animal
cats = animals;
// same ERROR
animals = cats;
End of story for Flow
aka Disjoint Unions aka Tagged Unions aka Algebraic data types
to describe data with weird shapes
Depending on some data other data might apply or not
type Response = Result | Error; // a disjoint union type with two cases
type Result = { status: 'done', payload: Object };
type Error = { status: 'error', message: string };
function callback(response: Response) {
// given by code completion
console.log(response.status);
// does not work,
// as we do not know if it exists, just yet
console.log(result.payload); // ERROR
switch (response.status) {
case 'done':
// this is the special thing:
// type system now knows, this is a Response
const result: Result = response;
console.log(result.payload);
break;
case 'error':
const error: Error = response;
console.log(error.message);
break;
}
}
class Person {
name: string;
}
class Dog {
name: string;
}
let dog: Dog = new Dog();
// nope, nominal type compatibility violated
// ERROR: Dog: This type is incompatible with Person
let person: Person = dog;
// same problem
// ERROR: object literal: This type is incompatible with Person
let person: Person = {
name: "Olli"
};
class Person {
name: string;
}
class Dog {
name: string;
}
let dog: Dog = new Dog();
// yes, correct, as structurally compatible
let person: Person = dog;
// same thing, also correct
let person: Person = {
name: "Olli"
};
interface NamedObject {
name: string;
}
// this is fine as nominal typing only applies to Flow classes
let namedObject: NamedObject = dog;
// same thing, also fine
let namedObject: NamedObject = {
name: "Olli"
};
// not fine in either, missing name
let namedObject: NamedObject = {
firstName: "Olli"
};
TypeScript has special support for classes
Makes it easier for people coming from Java/C++/C#
Flow does not feature those or any other syntactic sugar, as it is a checker only
Starting from 2016.3
My recommendation