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"