# Typescript

v4.2

# install
yarn add typescript
tsc index.js
tsc index.js -w # watch


# create tsconfig.json
tsc --init
# watch if tsconfig.json is configured
tsc -w

tsconfig.json

{
  "compilerOptions": {
    "target": "ES6",
    // "lib": ["dom", "es6"],
    "rootDir": "./src",
    "outDir": "./dist"
  }
}

# Everyday Types

# Types

Importance Types
High string, boolean, number, Array[]
Medium object{}, tuple, enum, Function
Low null, void, unknown
Avoid any, never, undefined

# Implicit/Explicit

// implicit - const vs let
const foo = 'hello world'; // foo: "hello world" - a Literal type
let foo = 'hello'; // foo: string

// Explicit types - only let
let foo: string = 'a string';
let foo: number = 5;
let foo: string[] = ['foo', 'bar'];

# Literal type

interface User {
  name: string;
  gender: 'male' | 'female' | 'unspecified'; // not string but specific values only
}

let user: User = {
  name: 'umesh',
  gender: 'male',
};

# Array

const foo: number[] = [1, 3];
const foo: readonly number[] = [1, 3];
const bar: Array<number> = [1, 3]; // same as above
const bar: ReadonlyArray<number> = [1, 3]; // push/pop not possible
const doo: [number, string] = [1, 'hello']; // TUPLE - fixed number(and types) of items

# Function type

// Ex - Function type
let foo: Function;
// foo = 3; // error

// Ex - () => {}
let myFunc: (a: number, b: number) => number = (x, y) => {
  return x + y;
};

// Ex - callback
let myFunc2: (a: number, cb: (x: number) => number) => number = (
  x,
  callback
) => {
  return callback(x);
};

let twice = (num: number) => num * 2;
let thrice = (num: number) => num * 3;
console.log(myFunc2(5, twice)); // 10
console.log(myFunc2(5, thrice)); // 15

# Object

const user: {name: string; age: number; gender?: string} = {
  name: 'umesh',
  age: 31,
  //   gender: 'male' // optional property
};

# Union

// Union function
let getLen = (params: string | string[]) => {
  return params.length;
};
getLen('foo'); // 3
getLen(['foo', 'bar']); // 2
getLen(23); // error

# Interfaces & Type Aliases

  • Almost similar.
  • Can be used together too.
// interface
interface UserInterface {
  id: number;
  name: string;
}

let user: UserInterface = {
  id: 23,
  name: 'umesh',
};
// type aliases
type ID = number;
type NAME = string;
type STATUS = 'Block' | 'Unblocked';

// EX
type User = {
  id: number;
  name: string;
};

// Ex - Tuple (a strict array)
type mytype = [string, number];
let x: mytype = ['foo', 23];

# Type vs Interface

  • type is immutable.
// allowed in interface
// both id & name will be required
interface UserInterface {
  id: number;
}
interface UserInterface {
  name: string;
}
// Error - duplicate identifier
type UserInterface = {
  id: number;
};
type UserInterface = {
  name: string;
};

# Type casting/assertion

  • Type assertion is not same as casting.
// --- ex
let page: unknown = '23';
let pageNumber = page as number;

// --- ex
let page: string = '23';
let pageNumber = (page as unknown) as number;

// --- ex
const x = 'hello' as number; // error
const x = ('hello' as unknown) as number; // works
// assertion means here code temporarily lies to compiler
// that {} is of type IUser since it will be set soon
const [user, setUser] = React.useState<IUser>({} as IUser);

// later...
setUser(newUser);

# null/undefined

  • use ! at end of variable - value isn’t null or undefined
  • see Working with DOM below

# Unknown type

// UNKNOWN
let foo: unknown;
let bar: string;

foo = 23;
foo = 'umesh';

// case 1
bar = foo; // error

// case 2
if (typeof foo === 'string') {
  bar = foo; // no error
}

# Never type

  • return type of function
function generateErrorVoid(message: string, code: number): void {
  // return ; // no error
  return ''; // error (string is returned)
}

function generateError(message: string, code: number): never {
  // return; // error
  throw {message: message, errCode: code}; // useful
}

# Interfaces

// first letter is capital
// class = User and interface = UserInterface
interface UserInterface {
  name: string;
  age: number;
  hobby?: number; // optional
  greet(): string; // return type
}
let user: UserInterface = {
  name: 'foo',
  age: 23,
  // error - greet is missing error
};

# Working with DOM

// Note - Use `.ts` file and not typescript Playground

// input
let input = document.querySelector('#name'); // error

// case 1 - add !
let input = document.querySelector('#name')!; // no error - adding ! means #name will be definately available during runtime

// case 2 - add type check
let input = document.querySelector('#name'); // just adding ! means #name will be definately available during runtime
if (input) {
  // code
}
let input = document.querySelector('#name') as HTMLInputElement;
let value = input.value;

// add listener
input.addEventListener('click', (event) => {
  let target = event.target as HTMLInputElement;
  console.log(target.value);
});

# Classes

  • private, protected, public
class User {
  id: number; // public is default
  protected name: string;
  private age: number;
  readonly something: string;
  static readonly blablah: string = 'hooohaaaa'; // User.blablah

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
    this.something = name + age; // readonly except in constructor
  }
}

let foo = new User('foooo', 23);
// interface
interface UserInterface {
  greet(): string;
}
class User implements UserInterface {
  // code something

  greet() {
    return 'Hello';
  }
}

// inheritance
class Admin extends User {
  // code
}

# Generics in Typescript

  • Generics allows us to provide different datatypes ie dynamic datatypes
  • <T> means we can provide different datatypes
// <T> - no constraint and T can be anything
// <T extends object> - constraint that T must be object only

// add constraint - <T> must be object
const addId = <T extends object>(obj: T) => {
  let id = Math.random().toString(16);
  return {
    id,
    ...obj,
  };
};

interface UserInterface {
  name: string;
}

let user: UserInterface = {
  name: 'foo',
};

// let result = addId(user); // works (implicit)
let result = addId<UserInterface>(user); // better reading (explicit)
// Generics with interface
interface UserInterface<T, V> {
  name: string;
  data: T;
  info: V;
}

const user1: UserInterface<{meta: string}, string> = {
  name: 'foo',
  data: {
    meta: 'bar',
  },
  info: 'hello',
};

# Enum

  • Recommend - Use for constants in application
// Enums
// Alternatives is to use Union of strings like 'On'|'Off'

enum StatusEnum {
  On, // can be capital letters too
  Off,
  Loading = 'Pending', // equal not colon (:)
}

// let foo: StatusEnum = 'On'; // not assignable
let foo: StatusEnum = StatusEnum.On;

console.log(StatusEnum.On); // 0
console.log(StatusEnum.Off); // 1
console.log(StatusEnum.Loading); // Pending

It is common to use Enums inside Interfaces

interface UserInterface {
  name: string;
  status: StatusEnum;
}
Last Updated: 12/24/2021, 9:56:33 AM