TypeScript
- official page: https://www.typescriptlang.org/
Basic Typesβ
any
void
unknown
boolean
number
string
Symbol
null
undefined
string[] /* or Array<string> */
[string, number] /* tuple */
false, 0, "", null, undefined, NaN // always false
Declarationsβ
let isDone: boolean;
const myName: string = 'Robert'; // assignment
const myName = 'Robert'; // type determined from the right side
const hobbies: string[] = ['Programming', 'Cooking']; // array
const address: [string, number] = ["Street", 99]; // tuple
let repeatType: 'repeat-x' | 'repeat-y' | 'no-repeat'; // union enum
let sprites: Sprite | Sprite[]; // union type
let spriteMesh: Sprite & Mesh; // intersection type
let myCar: any = 'BMW'; // any
const greeting = `Hello I'm ${userName}`; // template literal
Castingβ
const user = result as User;
const user = (<User>)result;
// we can cast nested objects as well in both wayss
const users: User[] = [
<Employee> {
firstname: 'Kunio',
surname: 'Otani',
role: 'Developer'
}
]
Functionsβ
// normal function
function add (a: number, b: number): number {
return a + b
}
// arrow function
const add = (a: number, b: number): number => a + b;
// this never returns anything
function neverReturns(): never {
throw new Error('An error!');
}
Enumeratorsβ
- we should avoid enums, as they don't transpile well
- can be avoided by using const enums:
const enum Color
- can be avoided by using const enums:
enum Color = {
Gray, // 0
Red, // 1
Green = 100, // 100
Blue, // 101
Yellow = 2 // 2
}
const myColor: Color = Color.Green
console.log(myColor); // Prints: 100
const colors = Object.keys(Color); // gets all enum keys
- a better solution is to use const objects with a values helper type:
const Direction = {
Up: 0,
Down: 1,
Left: 2,
Right: 3,
} as const;
type Values<T> = T[keyof T];
let direction: Values<typeof Direction>): void
Classesβ
class Point {
public x: number;
public y: number;
protected static instances = 0; // initial value
constructor(x: number, y: number) {
Point.instances++;
this.x = x;
this.y = y;
}
}
// abstract class
abstract class Shape {
abstract calcSize(): number;
}
Inheritance and interfacesβ
class Point {
protected x: number;
protected y: number;
}
class Point3D extends Point {
protected z: number;
}
// if possible, prefer types over interfaces!
interface Colored {
select(): void;
}
class Pixel extends Point implements Colored {
select(): void { ... }
}
Getters, settersβ
class Plant {
private _species: string = 'Default';
get species() {
return this._species;
}
set species(value: string) {
this._species = value;
}
}
// value access
new Plant().species = 'ferns';
Genericsβ
class Greeter<T> {
greeting: T;
constructor(message: T) {
this.greeting = message;
}
}
let greeter = new Greeter<string>('Hello, world');
// default parameters
class Greeter<T = any> {
greeting: T;
}
Other constructsβ
// type assertions
let len: number = (input as string).length;
let len: number = (<string> input).length;
// optional parameters
interface User {
name: string, // can be null but not undefined
age?: number // can be null or undefined
}
// optional chaining (if foo is undefined, it will not throw an error)
let x = foo?.bar.baz();
// nullish coalescing
let x = foo ?? bar; // if foo is undefined, assign bar
// destructuring declaration (will take 3 attributes from an output)
const { x, y, z } = calcPosition();
// dynamic keys
{ [key: string]: Object[] }
// type aliases
type Name = string | string[]
// function alias
type MyFunction = (param1: number, param2: number) => string;
// types for polymorphism
type Shape = Square | Rectangle | Circle;
// function types
function getUser(callback: (user: User) => any) { callback({...}) }
// default parameters
const greet = (name: string = 'Robert') => console.log(`Hello, ${name}`);
// array destructuring
const testResults: number[] = [3.89, 2.99, 1.38];
const [result1, result2, result3] = testResults;
// arrow functions
const myMultiply: (val1: number, val2: number) => number;
let myFunction = (val1: number, val2: number) => {
return val1 + val2;
}
// object types
let userData: { name: string, age: number } = {
name: 'Max'
};
// rest operator
function displayTags(targetElement: HTMLElement, ...tags: string[]) {
for(let i in tags) { // do something here }
}
displayTags(myElement, "tag1", "tag2", "tag3");
// variable swap
let x = 1;
let y = 2;
[x, y] = [y, x];
// && and || declaration operators
let ent: Entity | null;
let name = ent && ent.name; // name is of type string | null
let ent2 = ent || { name: "test" }; // ent will always contain name
// spread operator (good for shallow copy)
let copy = { ...original };
let merged = { ...foo, ...bar, ...baz };
// type as interface
type Point = {
x: number;
y: number;
}
Exports and Importsβ
export const myConst = 12345; // simple export
// we can export types as well
export type Color = {
red: number;
green: number;
blue: number;
}
// import
import { PI, calculateCircumference } from './src/circle' // imports selected types
// type import
import { type Color } from './types';
Advanced Genericsβ
Indexed typeβ
type Asset = {
type: string;
}
// assetType must be a string
const getAssetInfo = (assetType: Asset['type']) => {
}
Recordβ
- Record can be used for dictionaries (objects with a given key type and a value type)
- keyof typeof list all keys of an object
const Items = {
none: 'sth',
expired: 'sth',
}
// LabelMap can only contain keys from Items
const LabelMap: Record<keyof typeof Items, string> = {
none: 'default',
expired: 'default',
}
ValueOfKeyβ
- will get a type of an object property
type ValueOfKey<T, U> = {
[K in keyof T]: U extends K ? T[K] : never;
}[keyof T];
type Obj = {
a: string;
}
// val is 'string'
type val = ValueOfKey<Obj, a>;
Unpack Array Typeβ
- will get a type of an object array property
type UnpackArrayType<T, U> = ValueOfKey<T, U> extends unknown[]
? ValueOfKey<T, U>[number]
: never;
type Obj = {
a: string[];
}
type val = UnpackArrayType<Obj, a>;
Conditional Genericsβ
- we can use ternary operators in generics
// intrinsic elements are built-in elements like div, section, etc.
// infer type is basically a helper that is used inside the generic declaration
type ComponentProps<T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>> =
T extends JSXElementConstructor<infer P> // T is a React component with props P
? P
: T extends keyof JSX.IntrinsicElements // T is an intrinsic component
? JSX.IntrinsicElements[T]
: {} ;