letstr:string="Hello";letnum:number=123;str=num;// ERROR: `number` is not assignable to `string`num=str;// ERROR: `string` is not assignable to `number`
interface Point {
x: number,
y: number
}
class Point2D {
constructor(public x:number, public y:number){}
}
let p: Point;
// OK, because of structural typing
p = new Point2D(1,2);
interface Point2D {
x: number;
y: number;
}
interface Point3D {
x: number;
y: number;
z: number;
}
var point2D: Point2D = { x: 0, y: 10 }
var point3D: Point3D = { x: 0, y: 10, z: 20 }
function iTakePoint2D(point: Point2D) { /* do something */ }
iTakePoint2D(point2D); // exact match okay
iTakePoint2D(point3D); // extra information okay
iTakePoint2D({ x: 0 }); // Error: missing information `y`
let iTakeSomethingAndPassItAnErr
= (x: (err: Error, data: any) => void) => { /* do something */ };
iTakeSomethingAndPassItAnErr(() => null) // Okay
iTakeSomethingAndPassItAnErr((err) => null) // Okay
iTakeSomethingAndPassItAnErr((err, data) => null) // Okay
// ERROR: Argument of type '(err: any, data: any, more: any) => null' is not assignable to parameter of type '(err: Error, data: any) => void'.
iTakeSomethingAndPassItAnErr((err, data, more) => null);
let foo = (x:number, y: number) => { /* do something */ }
let bar = (x?:number, y?: number) => { /* do something */ }
let bas = (...args: number[]) => { /* do something */ }
foo = bar = bas;
bas = bar = foo;
/** Event Hierarchy */
interface Event { timestamp: number; }
interface MouseEvent extends Event { x: number; y: number }
interface KeyEvent extends Event { keyCode: number }
/** Sample event listener */
enum EventType { Mouse, Keyboard }
function addEventListener(eventType: EventType, handler: (n: Event) => void) {
/* ... */
}
// Unsound, but useful and common. Works as function argument comparison is bivariant
addEventListener(EventType.Mouse, (e: MouseEvent) => console.log(e.x + "," + e.y));
// Undesirable alternatives in presence of soundness
addEventListener(EventType.Mouse, (e: Event) => console.log((<MouseEvent>e).x + "," + (<MouseEvent>e).y));
addEventListener(EventType.Mouse, <(e: Event) => void>((e: MouseEvent) => console.log(e.x + "," + e.y)));
// Still disallowed (clear error). Type safety enforced for wholly incompatible types
addEventListener(EventType.Mouse, (e: number) => console.log(e));
/** Type Hierarchy */
interface Point2D { x: number; y: number; }
interface Point3D { x: number; y: number; z: number; }
/** Two sample functions */
let iTakePoint2D = (point: Point2D) => { /* do something */ }
let iTakePoint3D = (point: Point3D) => { /* do something */ }
iTakePoint3D = iTakePoint2D; // Okay : Reasonable
iTakePoint2D = iTakePoint3D; // Okay : WHAT
enum Status { Ready, Waiting };
let status = Status.Ready;
let num = 0;
status = num; // OKAY
num = status; // OKAY
enum Status { Ready, Waiting };
enum Color { Red, Blue, Green };
let status = Status.Ready;
let color = Color.Red;
status = color; // ERROR
class Animal {
feet: number;
constructor(name: string, numFeet: number) { /** do something */ }
}
class Size {
feet: number;
constructor(meters: number) { /** do something */ }
}
let a: Animal;
let s: Size;
a = s; // OK
s = a; // OK
/** A class hierarchy */
class Animal { protected feet: number; }
class Cat extends Animal { }
let animal: Animal;
let cat: Cat;
animal = cat; // OKAY
cat = animal; // OKAY
/** Looks just like Animal */
class Size { protected feet: number; }
let size: Size;
animal = size; // ERROR
size = animal; // ERROR
interface Empty<T> {
}
let x: Empty<number>;
let y: Empty<string>;
x = y; // okay, y matches structure of x
interface NotEmpty<T> {
data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;
x = y; // error, x and y are not compatible
let identity = function<T>(x: T): T {
// ...
}
let reverse = function<U>(y: U): U {
// ...
}
identity = reverse; // Okay because (x: any)=>any matches (y: any)=>any
class List<T> {
add(val: T) { }
}
class Animal { name: string; }
class Cat extends Animal { meow() { } }
const animals = new List<Animal>();
animals.add(new Animal()); // Okay
animals.add(new Cat()); // Okay
const cats = new List<Cat>();
cats.add(new Animal()); // Error
cats.add(new Cat()); // Okay
/** Hierarchy */
class Animal { constructor(public name: string){} }
class Cat extends Animal { meow() { } }
/** An item of each */
var animal = new Animal("animal");
var cat = new Cat("cat");
/**
* Demo : polymorphism 101
* Animal <= Cat
*/
animal = cat; // Okay
cat = animal; // ERROR: cat extends animal
/** Array of each to demonstrate variance */
let animalArr: Animal[] = [animal];
let catArr: Cat[] = [cat];
/**
* Obviously Bad : Contravariance
* Animal <= Cat
* Animal[] >= Cat[]
*/
catArr = animalArr; // Okay if contravariant
catArr[0].meow(); // Allowed but BANG 🔫 at runtime
/**
* Also Bad : covariance
* Animal <= Cat
* Animal[] <= Cat[]
*/
animalArr = catArr; // Okay if covariant
animalArr.push(new Animal('another animal')); // Just pushed an animal into catArr!
catArr.forEach(c => c.meow()); // Allowed but BANG 🔫 at runtime