Introduction to TypeScript (Part 4)

Generic Types

Generic Functions

function firstElement<Type>(arr: Type[]): Type | undefined {
return arr[0];
// In the below s is of type 'string':
const s = firstElement(["a", "b", "c"]);
// In the below n is of type 'number'
const n = firstElement([1, 2, 3])
// In the below u is of type 'undefined'
const u = firstElement[]

Type Inference

Multiple Generic Types

function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {

Constraining Generic Types

function longest<Type extends { length: number }>(a: Type, b: Type) {
if (a.length >= b.length) {
return a;
} else {
return b;

Note about Using Generic Types & Functions

Some more notes on Generic Functions

interface GenericFunction1<Type> {
(arg: Type): Type
interface GenericFunction2 {
<Type>(arg: Type): Type;
// myFunction1 must accept arguments of type numberlet myFunction1: GenericFunction1<Number>// myFunction2 must return the same argument as that passed inmyFunction2: GenericFunction2

Generic Object Types

interface Box<Type> { contents: Type }type Box<Type> = { contents: Type }// Either of the above will allow you to do:
let box: Box<string>
type OrNull<Type> = Type | null;

type OneOrMany<Type> = Type | Type[];

type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>;

type OneOrManyOrNull<Type> = OneOrMany<Type> | null

type OneOrManyOrNullStrings = OneOrManyOrNull<string>;

type OneOrManyOrNullStrings = OneOrMany<string> | null

Array Types

Generic Classes

class Box<Type, NumType> {
contents: Type;
add: (x: NumType, y: NumType) => NumType;
constructor(value: Type) {
this.contents = value;

const b = new Box("hello!");

Type Manipulation

  • By combining various types to express complex operations, we make our type system very maintable as we can modify certain parts and allow the changes to cascade down to all other types that rely on those parts
  • The more specialised our types get, the more bugs and incorrect programming flows we can pick up at compile time and not run time. This helps make our programs more robust (although type checking is not a panacea for all runtime bugs — just a tool to help reduce them)

Keyof Type Operator

type Point = { x: number; y: number };// This is the same as type p = "x" | "y"
type P = keyof Point;
type Arrayish = { [n: number]: unknown };// same as type A = number;
type A = keyof Arrayish;

Typeof Type Operator

let s = "hello";// same as let n: string
let n: typeof s;

Indexed Access Types

type Person = { age: number; name: string; alive: boolean };// Same as type Age = number
type Age = Person["age"];
// Same as type I1 = string | number
type I1 = Person["age" | "name"];
// Same as type I2 = string | number | boolean
type I2 = Person[keyof Person];
const MyArray = [ { name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 }, ];
// Same as type Person = { name: string, age: number }
type Person = typeof MyArray[number];
// Same as type Age = number
type Age = typeof MyArray[number]["age"]

Conditional Types

type Example1 = Dog extends Animal ? number : string;

Mapped & Template Literal Types

Rounding it Out: Importing & Exporting Types

export type Cat = { breed: string; yearOfBirth: number };import type { Cat, Dog } from "./animal.js";




Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Ashok Khanna

Ashok Khanna

Masters in Quantitative Finance. Writing Computer Science articles and notes on topics that interest me, with a tendency towards writing about Lisp & Swift