Type Transformation in Typescript Part 1

By NTh Hai - 10 min read

Familiarity with types transformation enables you to manipulate types precisely according to your requirements. As you gain a deeper comprehension of how these elements function in unison, you'll discover that you're writing less code that is simpler to upkeep.

Inference Basics

The TypeScript compiler uses type inference to automatically determine the data type of a value depending on the context in which it is used. We can use several utilities and operators to define our own types more precisely.

Get Return Type from Function

We can use utility type ReturnType to get return type of function

const myFunc = () => {
  return "hello";
};

type MyFunc = typeof myFunc;
// type MyFunc = () => string

//extract MyFuncReturn from myFunc?
type MyFuncReturn = ReturnType<MyFunc>;
// type MyFuncReturn = string

Get Parameter Type from Function

We can use utility type Parameters to get parameter types of function

const randomFunc = (
  url: string,
  opts?: {
    method?: string;
    headers?: {
      [key: string]: string;
    };
    body?: string;
  },
) => {};

type RandomFuncParameters = Parameters<typeof randomFunc>;
/* type RandomFuncParameters = [url: string, opts?: {
    method?: string | undefined;
    headers?: {
        [key: string]: string;
    } | undefined;
    body?: string | undefined;
} | undefined] */

Get return type from a Promise

We can use utility type Awaited to unwrap Promise and get the return type

const getUser = () => {
  return Promise.resolve({
    id: "123",
    name: "John",
    email: "john@example.com",
  });
};

type ReturnValue = Awaited<ReturnType<typeof getUser>>;
/* type ReturnValue = {
    id: string;
    name: string;
    email: string;
} */

Get Object keys

We use Keyof Type Operator on an object type and produce a string or numeric literal union of its keys

const testingFrameworks = {
  vitest: {
    label: "Vitest",
  },
  jest: {
    label: "Jest",
  },
  mocha: {
    label: "Mocha",
  },
};

type TestingFramework = keyof typeof testingFrameworks;
// type TestingFramework = "vitest" | "jest" | "mocha"

Get Object value type

We access object key directly from object type to get object value type:

export const fakeDataDefaults = {
  String: "Default string",
  Int: 1,
  Float: 1.14,
  Boolean: true,
  ID: "id",
};

export type StringType = typeof fakeDataDefaults["String"];
// type StringType = string
export type IntType = typeof fakeDataDefaults["Int"];
// type IntType = number
export type FloatType = typeof fakeDataDefaults["Float"];
// type FloatType = number
export type BooleanType = typeof fakeDataDefaults["Boolean"];
// type BooleanType = boolean
export type IDType = typeof fakeDataDefaults["ID"];
// type IDType = string

Unions Types

Union types play a key role in the type transformation process. In this section we will explore both unions and discriminated unions as well as built-in utility types that helps access their properties

Terminology

type A =
  | {
      type: "a";
      a: string;
    }
  | {
      type: "b";
      b: string;
    }
  | {
      type: "c";
      c: string;
    };

A is a discriminated union => discriminated unions have common properties which are used to differentiate between members of the union. In A case, it is the "type" properties.

type B = "a" | "b" | "c";

B is a union, but not a discrimated unions because B doesnt have a common properties to differentiate each member.

enum C {
 A = "a",
 B = "b",
 C = "c",
}

C is enum

Extract Member Type from discriminated Union

We can use Extract utility type to extract a member type of a union

export type Event =
 | {
     type: "click";
     event: MouseEvent;
   }
 | {
     type: "focus";
     event: FocusEvent;
   }
 | {
     type: "keydown";
     event: KeyboardEvent;
   };

type ClickEvent = Extract<Event, {type: "click"}>;
/*  type ClickEvent = {
     type: "click";
     event: MouseEvent;
}   */

Extract discriminator from discriminated union

We can extract discriminator by accessing directly the value of the common property

export type Event =
 | {
     type: "click";
     event: MouseEvent;
   }
 | {
     type: "focus";
     event: FocusEvent;
   }
 | {
     type: "keydown";
     event: KeyboardEvent;
   };

type EventType = Event["type"];
// type EventType = "click" | "focus" | "keydown"

Extract Object value as Literal

We can get object value as literal simply by using as const operator

export const programModeEnumMap = {
 GROUP: "group",
 ANNOUNCEMENT: "announcement",
 ONE_ON_ONE: "1on1",
 SELF_DIRECTED: "selfDirected",
 PLANNED_ONE_ON_ONE: "planned1on1",
 PLANNED_SELF_DIRECTED: "plannedSelfDirected",
} as const;

export type GroupProgram = typeof programModeEnumMap["GROUP"];
// type GroupProgram = "group"
export type AnnouncementProgram = typeof programModeEnumMap["ANNOUNCEMENT"];
// type AnnouncementProgram = "announcement"
export type OneOnOneProgram = typeof programModeEnumMap["ONE_ON_ONE"];
// type OneOnOneProgram = "1on1"
// ...

Get Array value as Literal

For array values, we can get their value as literals by as const operator and accessing their index directly. If we want to get union of all value of an array, we can simply pass number as their index

const fruits = ["apple", "banana", "orange"] as const;

type AppleOrBanana = typeof fruits[0|1];
// type AppleOrBanana = "apple" | "banana"
type Fruit = typeof fruits[number];
// type Fruit = "apple" | "banana" | "orange"

Comment Section

to comment