a very lightweight introduction
A way to tell how the data looks like at compile time.
Example with Java:
String name = "John Doe";
Type compatibility in TypeScript is based on structural typing.
interface Pet {
name: string;
}
class Dog {
name: string;
}
let pet: Pet;
// OK, because of structural typing
pet = new Dog();
this is also called duck typing 🦆
if it walks like a duck and it quacks like a duck, then it must be a duck
Pros:
Cons:
let variable: Type = value;
const y: number = 42;
const name: string = "foobar";
const isSomething: boolean = true;
const x = 27;
const name = "foo";
const isSomething = true;
const words: string[] = ["Foo", "Bar"];
const words: Array<string> = ["Foo", "Bar"];
const tuple: [string, number] = ["FooBar", 27];
function formatPrice(fn: (v: number) => string): string {}
function printName(obj: { first: string; last?: string }) {
// ...
}
printName({ first: "John", last: "Doe" });
printName({ first: "Bob" });
its like saying
please turn off type checking for this thing
similar to the any type, but is safer because it’s not legal to do anything with an unknown value
let maybe: unknown;
requires type guards to access the value
if (typeof maybe === "string") {
// maybe is a string
}
used for functions returning void
function noop(): void {}
function noop() {}
function noop() {
return;
}
some functions never
return a value
function fail(msg: string): never {
throw new Error(msg);
}
a way to build new types from existing ones using operators
function draw(shape: Square | Circle) {}
function draw(shape: Shape & Colorful) {}
a way to define a new type to be used in other places
type Person = {
name: string;
age: number;
height?: number;
};
type Shape = Square | Circle;
interface Person {
name: string;
age: number;
height?: number;
}
The recommended way to define a type is to use interface
, at least until you need to use features from type
.
type Alignment = "left" | "center" | "right";
enum Direction {
Left,
Right,
} // Left === 0, Right === 1
enum Direction {
Left = "LEFT",
Right = "RIGHT",
}
let strLength: number = (someValue as string).length;
let strLength: number = (<string>someValue).length;
its like saying
trust me, I know what I’m doing
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
readonly
class Person {
readonly name: string;
}
?
(optional)
class Person {
name: string;
age?: number;
}
public
| private
| protected
class Person {
public title: string;
protected name: string;
private age: number;
constructor(name: string, age: number, title: string) {
this.name = name;
this.age = age;
this.title = title;
}
public getFullName(): string {}
}
class Person {
static getFullName(person: Person): string {
return person.name + " " + person.title;
}
}
Person.getFullName({ name: "John", title: "Mr." });
abstract class Person extends Being implements Serializable {
static species = "Homo sapiens";
name: string;
constructor(name: string) {
super(Person.species);
}
serialize(): string {
return JSON.stringify(this);
}
}
(for manipulating types)
type Point = { x: number; y: number };
type P = keyof Point; // P = "x" | "y"
type Point = { x: number; y: number };
let p: Point = { x: 1, y: 2 };
let cType: typeof p.x; // cType = number
type Person = { age: number; name: string; alive: boolean };
type Age = Person["age"]; // Age = number
a way describe meaningful type dependencies
function firstElement<Type>(arr: Type[]): Type {
return arr[0];
}
function firstElement<T>(arr: T[]): T {
return arr[0];
}
const s = firstElement(["a", "b", "c"]); // s is a string
const n = firstElement([1, 2, 3]); // n is a number
class Box<C> {
contents: C[] = [];
add(content: C) {
this.contents.push(content);
}
getLast(): C | undefined {
return this.contents.pop();
}
}
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a");
getProperty(x, "m"); // not allowed
inferring the type of a variable by narrowing down the amount of types it can be
let someValue: number | string | undefined;
this is done with type guards
if (typeof someValue === "string") {
// do something with the string
}
if (someValue) {
// do something
}
function example(x: string | number, y: string | boolean) {
if (x === y) {
// We can now call any 'string' method on 'x' or 'y'.
}
}
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) {
return animal.swim();
}
return animal.fly();
}
function logValue(x: Date | string) {
if (x instanceof Date) {
console.log(x.toUTCString());
} else {
console.log(x.toUpperCase());
}
}
let x: number | string = "foo";
x = "bar";
// x is now a string
x = 1;
// x is now a number
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
const pet = getSmallPet();
if (isFish(pet)) {
pet.swim();
} else {
pet.fly();
}
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius * shape.radius;
case "square":
return shape.sideLength * shape.sideLength;
}
}
(just a sneak peek)
function connect(dest: Destination): Connection;
function connect(host: string, port: number): Connection;
function connect(
destOrHost: string | Destination, // any type of the above
port?: number // optional
): Connection {
if (typeof destOrHost === "string") {
// do something with host and port
} else {
// do something with a destination
}
}
const c1 = connect("localhost", 80);
const c2 = connect({ host: "localhost", port: 80 });
allows TypeScript compiler to validate calls to external code
get existing types
npm install --save @types/some-lib
implement your own
declare namespace 'some-lib' {
function makeSomething(s: string): string;
let numberOfThings: number;
}
@sealed
class Test {
@format("yyyy-MM-dd")
date: string;
@measure
@memoize
computeX(@required arg: string) {}
}
A mixin is a function that
function Timestamped<T extends Constructor>(Base: T) {
return class extends Base {
timestamp = Date.now();
};
}
const TimestampedPerson = Timestamped(Person);
const tp = new TimestampedPerson();
a way to transform code programatically
const Transformer = (code) => code;
⚠️ Here be dragons... And ASTs.
TSC is the TypeScript Compiler
tsconfig.json
npm install -g typescript
tsc index.ts # compiles index.ts to index.js
ts-node
is a TypeScript execution engine and REPL for Node.js.
npm install -g ts-node
ts-node index.ts # compiles index.ts and runs it
A Code Editor The Code Editor
A secure runtime for JavaScript and TypeScript.
when something wrong is not right
Argument of type '{ foo: number; bar: () => string; }' is not
assignable to parameter of type 'SomethingComplex'.
Types of property 'bar' are incompatible.
Type '() => string' is not assignable to type 'string'.
Digesting the error
What?
the description of the error is in the first line
Why?
starting in line 2, there is a chain of causes of the error
any
Function
number
vs Number