Slides for this talk: http://bit.ly/types-jax
// variables can have type information
let foo: string;
foo = 'yo';
// Error: number: This type is incompatible with string
foo = 10;
// types can be explicit (parameter) or inferred (return type)
function sayIt(what: string) {
return `Saying: ${what}`;
}
const said: string = sayIt(obj);
class Sayer {
what: string; // type also mandatory
constructor(what: string) {
this.what = what;
}
// return type if you want to
sayIt(): string {
return `Saying: ${this.what}`;
}
}
// variables can have type information
let foo: string;
foo = 'yo';
// Error: Type 'number' is not assignable to type 'string'.
foo = 10;
// types can be inferred (return type)
function sayIt(what: string) {
return `Saying: ${what}`;
}
const said: string = sayIt(obj);
class Sayer {
what: string; // mandatory
constructor(what: string) {
this.what = what;
}
// return type if you want to
sayIt(): string {
return `Saying: ${this.what}`;
}
}
Those basic features help with documentation, refactoring, and IDE support
One of my main sources of runtime exceptions when programming Java
Even after many years it is still surprising how many corner cases I miss in complex code
what is the result here in pure JavaScript?
function foo(num) {
if (num > 10) {
return 'cool';
}
}
console.log(foo(9).toString());
"Uncaught TypeError: Cannot read property 'toString' of undefined"
What the flow checker thinks about this
// 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 = foo(9);
if (fooed) {
fooed.toString();
}
Types are non-nullable by default in flow
// both TypeScript and flow allow
// to put the type annotation here instead of using inference
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 (might be a bad idea)
fooed!.toString();
Only applies to TypeScript 2.x
Only works when strictNullChecks option is checked
All types nullable by default in TypeScript 1.x
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'));
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');
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
// a disjoint union type with two cases
type Response = Result | Failure;
type Result = { status: 'done', payload: Object }; // all good, we have the data
type Failure = { status: 'error', code: number}; // error, we get the error code
type Result = { status: 'done', payload: Object };
type Failure = { status: 'error', code: number};
function callback(response: Result | Failure) {
// works, as this is present in both
console.log(response.status);
// does not work,
// as we do not know if it exists, just yet
console.log(response.payload); // ERROR
console.log(response.code); // ERROR
switch (response.status) {
case 'done':
// this is the special thing:
// type system now knows, this is a Result
console.log(response.payload);
break;
case 'error':
// and this is a Failure
console.log(response.code);
break;
}
}
class Person {
name: string;
}
class Dog {
name: string;
}
let dog: Dog = new Dog();
// nope, nominal type compatibility violated
let person: Person = dog; // ERROR: Dog: This type is incompatible with Person
// same problem
let person: Person = { // ERROR: object literal: This type is incompatible with 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
Similar features can be found in Java/C++/C#
Flow does not feature those (except for Interfaces) or any other syntactic sugar, as it is a checker only
https://github.com/DJCordhose/react-workshop/tree/master/code/sandbox/typescript
https://github.com/DJCordhose/react-workshop/tree/master/code/sandbox/flow
Some React Hacking with TypeScript
Both Flow and TypeScript work great with on Browser and Server