January 30, 2024

TypeScript for JavaScript

JavaScript in NOT Statically Typed, JS is Dynamically Typed Language

TypeScript is a free and open-source high-level programming language developed by Microsoft
that adds static typing with optional type annotations to JavaScript.

It is designed for the development of
large applications and transpiles to JavaScrip

*** Statically Typed Languages In statically typed programming languages, type checking occurs at compile time.

*** Dynamically Typed Languages Conversely, in dynamically typed languages, type checking takes place at runtime or execution time.


✅ Advantages of Dynamic Typing in JavaScript

❌ Disadvantages of Dynamic Typing in JavaScript

The most significant impact of TypeScript

Reducing Runtime Errors

Other Befefits of Typescristusing in JS

JS + TypeScript Type-Safe Code

Let’s examine the basic concepts and examples of how we can use them.

var

// Boolean
let isDone: boolean = false;
 
// String
let color: string = "red";
color = 1; // ❌  Type 'number' is not assignable to type 'string'
 
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}.
I'll be ${age + 1} years old next month.`;

Array

// All elements as a number
let list: number[] = [1, 2, 3];
 
// or with Generic
let anotherList: Array<number> = [1, 2, 3];

Tuple

let data: [string, number];
 
data = ["admin", 29];

Enum (Enumeration) in TypeScript

TypeScript does not create a new object in memory for each instance. Instead, an enum

enum StatusCode {
  OK = 200,
  REDIRECT = 300,
  BAD_REQUEST = 400,
  SERVER_ERROR = 500,
}
 
let responseStatus: StatusCode = StatusCode.OK;

Any - same as turn OFF - TypeScript

❌ - not recommended to use

let item: any = 1;
item = "string";
item = false;

Function in TypeScript

/* Case 1: Function that returns nothing */
function initFunction(): void {
  console.log("Hello");
}
 
/* Case 2: Function that returns a number */
function sum(): number {
  return 1 + 1;
}
 
/* Case 2: with params */
const show = (msg: string) => {
  console.log(msg);
};
 
/* Case : destructured parameters and TypeScript Tuple type annotation */
function f([first, second]: [number, number]) {
  console.log(first);
  console.log(second);
}
f([1, 2]);
 
/* Case : Typed result of function */
function add(x: number, y: number): number {
  return x + y;
}
const sum: number = add(1, 2);

undefined, null

let n1: undefined = undefined;
let n2: null = null;

never in TypeScript

let core: never = (() => {
  console.log(true);
  throw new Error("Some Error");
})();
 
function throwError(message: string): never {
  throw new Error(message);
}

Object in TypeScript

let obj: object | null;
 
obj = null;
obj = {
  n: 1,
};
 
console.log(obj);

Okay, now let’s dive into some more interesting tips and advanced examples of TypeScript usage:

Type assertions

let someString: any = "Some string";
 
let size: number = (<string>someString).length; // I want to treat it as a "string"
// or
let size: number = (someString as string).length;
/* name?: string indicates that the name property is optional. */
type Schema = {
  name?: string;
};
type Schema = { a: string; b?: number };
 
function init({ a, b = 0 }: Schema = { a: "" }): void {
  console.log(a);
  console.log(b);
}
 
init({ a: "str" });

Best Practice: Type signature

type FType = (baseValue: number, increment: number) => number;
 
const increase: FType = function (x: number, y: number): number {
  return x + y;
};
 
const updatedValue: number = increase(3, 1);

Rest JS + Tuple

function buildLetters(
  firstLetter: string,
  ...restOfLetters: [string, string, string]
) {
  return firstLetter + " " + restOfLetters.join(" ");
}
 
// or
type customType = string | number;
 
function buildLetters(firstLetter: string, ...restOfLetters: customType[]) {
  return firstLetter + " " + restOfLetters.join(" ");
}
 
let letters = buildLetters("a", "b", "c", "d");

Good prictice to set up the Limit

when you want to ensure that the function is not called with a meaningful “this” context.

const run: (this: void, n: number) => void = function (n) {
  // this.n = n; // Property 'n' does not exist on type 'void'.
  console.log(n);
};
 
run(1);

Best Practice: Generics in TypeScript

/* Case: fun takes any type paramenter */
 
const returnValueByGeneric = function <T>(arg: T): T {
  return arg;
};
// - <T> as alias
// it is type variable that captures the type passed to the function
 
const text: string = returnValueByGeneric<string>("str"); // <string> - argument
 
const n: number = returnValueByGeneric<number>(1);

User-Defined Type Guards “instanceof”

const idEntityNumber = (val) => val instanceof Number;
 
const readLength = function <T>(arg: T[]): T[] {
  const n = arg[1];
 
  if (idEntityNumber(n)) {
    // Guards
    // or in JS typeof n === "number"
 
    console.log(n.toFixed());
  }
  return arg;
};
 
readLength<number>([1, 2, 3]);
const getArgument = function <T>(arg: T): T {
  return arg;
};
 
const anotherFunction: <T>(arg: T) => T = getArgument;
// number as T
const n: number = <number>anotherFunction(1);
 
// string as T
const text: string = <string>anotherFunction("str");

Generics with two different type variables, T and U.

const mix = function <T, U>(number1: T, number2: T, text: U): void {
  return `${text}: ${number1}, ${number2}`;
};
 
const anotherFunction: <T, U>(number1: T, number2: T, text: U) => void = mix;
 
anotherFunction<number, string>(1, 2, "List");

link - Google TypeScript Style Guide