Skip to content

REUSABLE TYPES · LESSON 11 OF 24

Utility types for application contracts

Create focused input, output and lookup types without copying fields.

  • Pick
  • Omit
  • Partial
  • Record

Your runnable checkpoint

Extract into a new practice folder. Each download includes complete source, package.json, tsconfig.json and an offline lesson. Use Node.js 22 or 24. Each checkpoint is independent.

Install with npm install. Run npm run check, then npm run build. Terminal lessons use npm start; browser lessons use npm run serve and http://127.0.0.1:4173.

1. Understand the concepts

Utility types transform existing types. Pick selects fields, Omit excludes fields, Partial makes top-level fields optional, Required makes them required, and Readonly prevents assignment through a reference. These are shallow transformations.

A patch type should describe only fields a caller is allowed to change. Partial<User> would permit changing the id too. Select the editable fields first, then make that selection partial.

Record<Key, Value> describes values for a set of keys. A finite key union requires each key. Record<string, Value> is an open dictionary; arbitrary lookups can be absent, especially under noUncheckedIndexedAccess.

Exclude and Extract filter union members; NonNullable removes null and undefined. Parameters and ReturnType inspect function signatures, while Awaited unwraps promise-like types. None of these types removes private data from a runtime object; construct public responses explicitly.

2. Build and run

These are complete files. Preserve the paths shown below, including the src directory. The package and compiler configuration are included at the end.

src/main.ts

src/main.ts
type User = { id: number; name: string; email: string; role: "student" | "trainer" };
type UserPatch = Partial<Pick<User, "name" | "email">>;
type PublicUser = Omit<User, "email">;
function update(user: User, patch: UserPatch): User {
  return { ...user, ...patch };
}
const user: User = { id: 1, name: "Asha", email: "a@example.com", role: "student" };
const edited = update(user, { name: "Asha Rao" });
const publicUser: PublicUser = { id: edited.id, name: edited.name, role: edited.role };
const labels: Record<User["role"], string> = { student: "Learner", trainer: "Teacher" };
console.log(publicUser.name);
console.log(labels[user.role]);

package.json

package.json
{
  "name": "edupoly-typescript-utility-types",
  "private": true,
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "check": "tsc --noEmit",
    "build": "tsc",
    "start": "node dist/main.js"
  },
  "devDependencies": {
    "typescript": "5.9.3"
  }
}

tsconfig.json

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": [
      "ES2022",
      "DOM"
    ],
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noEmitOnError": true,
    "verbatimModuleSyntax": true,
    "rootDir": "src",
    "outDir": "dist",
    "sourceMap": true,
    "types": [],
    "moduleDetection": "force"
  },
  "include": [
    "src/**/*"
  ]
}

3. Trace the code and check the result

  1. UserPatch excludes id and role before making the remaining properties optional.
  2. The spread creates a new object rather than mutating user.
  3. The public object is built explicitly so email is actually absent at runtime.

Expected result

Asha Rao
Learner

4. Practice independently

Create a type for the arguments and result of update.

Show the explanation

Use type UpdateArgs = Parameters<typeof update> and type UpdatedUser = ReturnType<typeof update>. These follow changes to the function signature.

When something goes wrong

If tsc is missing, run npm install in the folder containing package.json. If the output looks old, rebuild before running. Read the first compiler diagnostic and check the indicated field or argument. For module errors, preserve the .js extensions in imports. Browser examples must run through the included local server, not directly as TypeScript files.

Read the related official TypeScript documentation ↗