Typescript cheatsheet

Types

const isFetching: boolean = true
const isLoading: boolean = false

const int: number = 42
const float: number = 4.2
const num: number = 3e10

const message: string = 'Hello Typescript'

const numberArray: number[] = [1, 1, 2, 3, 5, 8, 13]
const numberArray2: Array<number> = [1, 1, 2, 3, 5, 8, 13]

const words: string[] = ['Hello', 'Typescript']

// Tuple
const contact: [string, number] = ['Audrius', 1234567]

// Any
let variable: any = 42
// ...
variable = 'New String'
variable = []

// ====
function sayMyName(name: string): void {
  console.log(name)
}
sayMyName('Audrius')

// Never
function throwError(message: string): never {
  throw new Error(message)
}

function infinite(): never {
  while (true) {

  }
}

// Type
type Login = string

const login: Login = 'admin'
// const login2: Login = 2

type ID = string | number
const id1: ID = 1234
const id2: ID = '1234'
// const id3: ID = true

type SomeType = string | null | undefined
});

Interfaces

interface Rect {
  readonly id: string;
  color?: string;
  size: {
    width: number;
    height: number;
  };
}

const rect1: Rect = {
  id: "1234",
  size: {
    width: 20,
    height: 30,
  },
  color: "#ccc",
};

const rect2: Rect = {
  id: "12345",
  size: {
    width: 10,
    height: 5,
  },
};

rect2.color = "black";
// rect2.id = '3232'

const rect3 = {} as Rect;
const rect4 = <Rect>{};

// =====================

interface RectWithArea extends Rect {
  getArea: () => number;
}

const rect5: RectWithArea = {
  id: "123",
  size: {
    width: 20,
    height: 20,
  },
  getArea(): number {
    return this.size.width * this.size.height;
  },
};

// ==================

interface IClock {
  time: Date;
  setTime(date: Date): void;
}

class Clock implements IClock {
  time: Date = new Date();

  setTime(date: Date): void {
    this.time = date;
  }
}

// =================

interface Styles {
  [key: string]: string;
}

const css: Styles = {
  border: "1px solid black",
  marginTop: "2px",
  borderRadius: "5px",
};

Enum

enum Membership {
  Simple,
  Standard,
  Premium,
}

const membership = Membership.Standard;
const membershipReverse = Membership[2];

console.log(membership);
console.log(membershipReverse);

enum SocialMedia {
  FACEBOOK = "FACEBOOK",
  INSTAGRAM = "INSTAGRAM",
}

const social = SocialMedia.INSTAGRAM;
console.log(social);

Functions

function add(a: number, b: number): number {
  return a + b;
}

function toUpperCase(str: string): string {
  return str.trim().toUpperCase();
}

interface MyPosition {
  x: number | undefined;
  y: number | undefined;
}

interface MyPositionWithDefault extends MyPosition {
  default: string;
}

function position(): MyPosition;
function position(a: number): MyPositionWithDefault;
function position(a: number, b: number): MyPosition;

function position(a?: number, b?: number) {
  if (!a && !b) {
    return { x: undefined, y: undefined };
  }

  if (a && !b) {
    return { x: a, y: undefined, default: a.toString() };
  }

  return { x: a, y: b };
}

console.log("Empty: ", position());
console.log("One param: ", position(42));
console.log("Two params: ", position(10, 15));

Classes

class Typescript {
  version: string;

  constructor(version: string) {
    this.version = version;
  }

  info(name: string) {
    return `[${name}]: Typescript version is ${this.version}`;
  }
}

// class Car {
//   readonly model: string
//   readonly numberOfWheels: number = 4
//
//   constructor(theModel: string) {
//     this.model = theModel
//   }
// }

class Car {
  readonly numberOfWheels: number = 4;
  constructor(readonly model: string) {}
}
// ==============

class Animal {
  protected voice: string = "";
  public color: string = "black";

  constructor() {
    this.go();
  }

  private go() {
    console.log("Go");
  }
}

class Cat extends Animal {
  public setVoice(voice: string): void {
    this.voice = voice;
  }
}

const cat = new Cat();
cat.setVoice("test");
console.log(cat.color);
// cat.voice

// =====================

abstract class Component {
  abstract render(): void;
  abstract info(): string;
}

class AppComponent extends Component {
  render(): void {
    console.log("Component on render");
  }

  info(): string {
    return "This is info";
  }
}

Guards

function strip(x: string | number) {
  if (typeof x === "number") {
    return x.toFixed(2);
  }
  return x.trim();
}

class MyResponse {
  header = "response header";
  result = "response result";
}

class MyError {
  header = "error header";
  message = "error message";
}

function handle(res: MyResponse | MyError) {
  if (res instanceof MyResponse) {
    return {
      info: res.header + res.result,
    };
  } else {
    return {
      info: res.header + res.message,
    };
  }
}

// ===================

type AlertType = "success" | "danger" | "warning";

function setAlertType(type: AlertType) {
  // ....
}

setAlertType("success");
setAlertType("warning");

// setAlertType('default')

Operators

interface Person {
  name: string;
  age: number;
}

type PersonKeys = keyof Person; // 'name' | 'age'

let key: PersonKeys = "name";
key = "age";

type User = {
  _id: number;
  name: string;
  email: string;
  createdAt: Date;
};

// 'name' | 'email'
type UserKeysNoMeta1 = Exclude<keyof User, "_id" | "createdAt">;
// 'name' | 'email'
type UserKeysNoMeta2 = Pick<User, "name" | "email">;

let u1: UserKeysNoMeta1 = "name";
// u1 = '_id'

Generics

const arrayOfNumbers: Array<number> = [1, 1, 2, 3, 5];
const arrayOfStrings: Array<string> = ["Hello", "Audrius"];

function reverse<T>(array: T[]): T[] {
  return array.reverse();
}

reverse(arrayOfNumbers);
reverse(arrayOfStrings);

// const cars: string[] = ['Ford', 'Audi']
// const cars2: Array<string> = ['Ford', 'Audi']

// const promise: Promise<number> = new Promise(resolve => {
//   setTimeout(() => {
//     resolve(42)
//   }, 2000)
// })
//
// promise.then(data => {
//   console.log(data.toFixed())
// })

function mergeObjects<T extends object, R extends object>(a: T, b: R) {
  return Object.assign({}, a, b);
}

const merged = mergeObjects({ name: "Audrius" }, { age: 26 });
const merged2 = mergeObjects({ model: "Ford" }, { year: 2010 });

// const merged3 = mergeObjects({a: 1}, 'bbb')
// console.log(merged3)

// =================

interface ILength {
  length: number;
}

function withCount<T extends ILength>(value: T): { value: T; count: string } {
  return {
    value,
    count: `Object has ${value.length} symbols`,
  };
}

// console.log(withCount('Hi typescript'))
// console.log(withCount(['I', 'Am', 'Array']))
// console.log(withCount(20))
// console.log(withCount({length: 20}))

// ==============

// function getObjectValue<T extends object, R extends keyof T>(obj: T, key: R) {
//   return obj[key]
// }
//
// const person = {
//   name: 'Audrius',
//   age: 26,
//   job: 'Javascript'
// }
// console.log(getObjectValue(person, 'name'))
// console.log(getObjectValue(person, 'age'))
// console.log(getObjectValue(person, 'job'))

// ===============

class Collection<T extends number | string | boolean> {
  constructor(private _items: T[] = []) {}

  add(item: T) {
    this._items.push(item);
  }

  remove(item: T) {
    this._items = this._items.filter((i) => i !== item);
  }

  get items(): T[] {
    return this._items;
  }
}

// const strings = new Collection<string>(['I', 'Am', 'Strings'])
// strings.add('!')
// strings.remove('Am')
// console.log(strings.items)
//
// const numbers = new Collection<number>([1, 2, 3])
// numbers.add(2)
// numbers.remove(3)
// console.log(numbers.items)

// const objs = new Collection([{a: 1}, {b: 2}])
// // objs.remove({b: 2})
// // console.log(objs.items)

// ===============

interface Car {
  model: string;
  year: number;
}

function createAndValidateCar(model: string, year: number): Car {
  const car: Partial<Car> = {};

  if (model.length > 3) {
    car.model = model;
  }

  if (year > 2000) {
    car.year = year;
  }

  return car as Car;
}

///

const cars: Readonly<Array<string>> = ["Ford", "Audi"];
// cars.shift()
// cars[1]

const ford: Readonly<Car> = {
  model: "Ford",
  year: 2020,
};

// ford.model = 'Ferrari'

Decorators

// function Log(constructor: Function) {
//   console.log(constructor)
// }
//
// function Log2(target: any, propName: string | Symbol) {
//   console.log(target)
//   console.log(propName)
// }
//
// function Log3(target: any, propName: string | Symbol, descriptor: PropertyDescriptor) {
//   console.log(target)
//   console.log(propName)
//   console.log(descriptor)
// }

// interface ComponentDecorator {
//   selector: string
//   template: string
// }
//
// function Component(config: ComponentDecorator) {
//   return function
//     <T extends { new(...args: any[]): object } >
//   (Constructor: T) {
//     return class extends Constructor {
//       constructor(...args: any[]) {
//         super(...args)
//
//         const el = document.querySelector(config.selector)!
//         el.innerHTML = config.template
//       }
//     }
//   }
// }
//
// function Bind(_: any, _2: any, descriptor: PropertyDescriptor): PropertyDescriptor {
//   const original = descriptor.value
//
//   return {
//     configurable: true,
//     enumerable: false,
//     get() {
//       return original.bind(this)
//     }
//   }
// }
//
// @Component({
//   selector: '#card',
//   template: `
//     <div class="card">
//       <div class="card-content">
//         <span class="card-title">Card Component</span>
//       </div>
//     </div>
//   `
// })
// class CardComponent {
//   constructor(public name: string) {
//   }
//
//   @Bind
//   logName(): void {
//     console.log(`Component Name: ${this.name}`)
//   }
// }
//
// const card = new CardComponent('My Card Component')
//
// const btn = document.querySelector('#btn')!
//
// btn.addEventListener('click', card.logName)

// ================

type ValidatorType = "required" | "email";

interface ValidatorConfig {
  [prop: string]: {
    [validateProp: string]: ValidatorType;
  };
}

const validators: ValidatorConfig = {};

function Required(target: any, propName: string) {
  validators[target.constructor.name] = {
    ...validators[target.constructor.name],
    [propName]: "required",
  };
}

function validate(obj: any): boolean {
  const objConfig = validators[obj.constructor.name];
  if (!objConfig) {
    return true;
  }
  let isValid = true;
  Object.keys(objConfig).forEach((key) => {
    if (objConfig[key] === "required") {
      isValid = isValid && !!obj[key];
    }
  });
  return isValid;
}

// class Form {
//   @Required
//   public email: string | void
//
//   constructor(email?: string) {
//     this.email = email
//   }
// }
//
// const form = new Form('[email protected]')
//
// if (validate(form)) {
//   console.log('Valid: ', form)
// } else {
//   console.log('Validation Error')
// }
//

Namespaces

// form-namespace.ts
namespace Form {
  export type FormType = "inline" | "block";
  export type FormState = "active" | "disabled";

  export interface FormInfo {
    type: FormType;
    state: FormState;
  }
}

/// <reference path="form-namespace.ts" />

namespace Form {
  class MyForm {
    private type: FormType = "inline";
    private state: FormState = "active";

    constructor(public email: string) {}

    getInfo(): FormInfo {
      return {
        type: this.type,
        state: this.state,
      };
    }
  }

  export const myForm = new MyForm("[email protected]");
}

console.log(Form.myForm);

// console.log(myForm)