広告が嫌いですか? 行く 広告なし 今日

TypeScriptのユーティリティ型 — 適切に覚えておくべきもの(およびそれ以外)

更新日

部分的、選択、省略、記録、Return Type、パラメータ、Awaited — 8つのユーティリティ型で、前後例と実際に毎週使うべきかどうかについての誠実な見解。

TypeScript Utility Types — The Ones Worth Memorizing (and the Rest) 1

You’ve probably read a TypeScript docs page that lists 20+ utility types in alphabetical order, defined each one in a sentence, and left you wondering which ones to actually internalize versus which ones exist for people writing framework internals. This is the more honest version: eight types, concrete before/after examples, and a straight answer on daily use versus occasionally useful.

If you’re starting from raw JSON and haven’t written your interfaces yet, the JSON to TypeScript converter will get you to a base interface in seconds — then come back here to layer these utility types on top.

Partial<T> — Weekly use. Learn it first.

Makes every property in T optional. The canonical use case is PATCH-style updates, where you only send the fields that changed.

// Before: manually duplicating the interface with optional versions of everything
interface UserUpdate {
  name?: string;
  email?: string;
  age?: number;
}

// After: Partial derives it from the source of truth
interface User {
  name: string;
  email: string;
  age: number;
}

function updateUser(id: string, updates: Partial<User>) {
  // updates.name is string | undefined — TypeScript knows
}

The problem with the “Before” version: if you add a field to User, you have to remember to add it to UserUpdate too. With Partial<User>, adding a field to the source interface automatically makes it available in updates. The types stay in sync.

Required<T> — Monthly use, but saves you in one specific scenario.

The inverse of Partial — strips all ? from a type. Most useful after validation: you receive a loosely-typed config object, validate that all fields are present, and then want TypeScript to stop suggesting that everything might be undefined.

// Before: after validating, you still fight undefined checks everywhere
interface Config {
  apiUrl?: string;
  timeout?: number;
  retries?: number;
}

function initApp(config: Config) {
  config.apiUrl?.trim(); // need optional chaining even after checking
}

// After: Required signals "this has been validated, stop warning me"
function validateAndInit(raw: Config): void {
  if (!raw.apiUrl) throw new Error("apiUrl required");
  if (!raw.timeout) throw new Error("timeout required");
  if (!raw.retries) throw new Error("retries required");

  const config = raw as Required<Config>;
  config.apiUrl.trim(); // no optional chaining needed
}

The honest note: Required<T> doesn’t do runtime validation — you still need to write the checks. It just changes what TypeScript believes downstream. Use it to communicate “this has been validated” at the type level.

Pick<T, K> — Weekly use. Name what you want.

Creates a new type containing only the properties you name from T. Useful when you want a subset of a larger interface — public API shapes, view models, serialized responses.

// Before: manually maintaining a separate interface that drifts from User
interface UserPublicProfile {
  name: string;
  bio: string;
  avatarUrl: string;
}

// After: Pick stays in sync with the source
interface User {
  id: string;
  name: string;
  email: string;       // private — shouldn't go to clients
  passwordHash: string;  // definitely private
  bio: string;
  avatarUrl: string;
}

type UserPublicProfile = Pick<User, "name" | "bio" | "avatarUrl">;

Pick is the right tool when the list of properties you want is shorter than the list you’d need to exclude. When it flips the other way, reach for Omit.

Omit<T, K> — Weekly use. Name what you don’t want.

Complement of Pick — creates a type with all properties of T except the ones you name. When you have a large interface and only a few fields to exclude, Omit makes the intent clearer than listing everything you do want.

// Before: manually listing every field except the sensitive ones
// Easy to miss a new field added to User later
interface UserForClient {
  id: string;
  name: string;
  email: string;
  bio: string;
  avatarUrl: string;
  // passwordHash intentionally omitted — but a future dev might not know that
}

// After: Omit states the exclusion explicitly
type UserForClient = Omit<User, "passwordHash">;
// If User gains a new "address" field, UserForClient gets it automatically

One thing neither Pick nor Omit does: change the type of individual properties. If you want to pick fields transform their types, you’re looking at a mapped type, which is a different tool entirely.

Record<K, V> — Weekly use. The typed dictionary.

Creates an object type where all keys are type K and all values are type V. The real win comes when K is a union type — TypeScript will error if you’re missing a key. Generic index signatures silently accept incomplete objects; Record with a union key doesn’t.

// Before: index signature with no exhaustiveness checking
const rolePermissions: { [key: string]: string[] } = {
  admin: ["read", "write", "delete"],
  editor: ["read", "write"],
  // forgot "viewer" — TypeScript doesn't notice
};

// After: Record with a union key enforces completeness
type Role = "admin" | "editor" | "viewer";

const rolePermissions: Record<Role, string[]> = {
  admin: ["read", "write", "delete"],
  editor: ["read", "write"],
  viewer: ["read"],
  // TypeScript error if any Role is missing
  // TypeScript error if you try to add an unknown key
};

The exhaustiveness check is the real win. It matters when the keys represent something meaningful like roles, event types, HTTP methods, or status codes — anything where a missing case is a bug, not just an omission.

ReturnType<F> — Weekly use if you consume third-party functions.

Extracts the return type of a function. Most useful when consuming a library function and wanting to type a variable that holds its output — without having to import the return type separately (which libraries don’t always export cleanly).

// Before: manually annotating the return type — it can drift
function getCurrentUser() {
  return { id: "u1", name: "Alice", roles: ["admin"] as const };
}

let cachedUser: { id: string; name: string; roles: readonly string[] };
// If getCurrentUser's return changes, this annotation doesn't update automatically

// After: ReturnType derives it and keeps in sync
type CurrentUser = ReturnType<typeof getCurrentUser>;
let cachedUser: CurrentUser;

// Especially useful with unexported library types:
import { useQuery } from "@tanstack/react-query";
type QueryResult = ReturnType<typeof useQuery>;
// No need to figure out what QueryObserverResult<...> looks like or import it

Parameters<F> — Occasional use. Mostly for wrappers.

Extracts the parameter types of a function as a tuple. Honest take: you won’t reach for this weekly unless you write a lot of wrapper functions or higher-order utilities. But when you need it, there’s no clean alternative.

// Before: manually duplicating parameter types in the wrapper
function createEvent(
  type: string,
  payload: Record<string, unknown>,
  timestamp: number
) { /* ... */ }

// Every time createEvent changes, this wrapper needs a manual update:
function loggedCreateEvent(
  type: string,
  payload: Record<string, unknown>,
  timestamp: number
) {
  console.log("Creating event:", type);
  return createEvent(type, payload, timestamp);
}

// After: Parameters keeps the wrapper in sync automatically
function loggedCreateEvent(...args: Parameters<typeof createEvent>) {
  console.log("Creating event:", args[0]);
  return createEvent(...args);
}

The spread + rest pattern (...args: Parameters<F>) is the main idiom. It also shows up in test mocks: jest.fn<ReturnType<F>, Parameters<F>>() gives you a fully typed mock without importing the function’s signature separately.

Awaited<T> — Use when you need to unwrap Promise types without calling them.

Added in TypeScript 4.5 (released November 2021). Recursively unwraps Promise<T> to get at T. Before it existed, getting the resolved type of an async function meant writing a conditional type: ReturnType<F> extends Promise<infer U> ? U : never — which breaks on double-wrapped Promises and is unpleasant to read in diffs.

// Before: conditional type to unwrap — breaks on nested Promises
async function fetchUser(id: string) {
  const res = await fetch(`/api/users/${id}`);
  return res.json() as { id: string; name: string; email: string };
}

type FetchedUser =
  ReturnType<typeof fetchUser> extends Promise<infer U> ? U : never;
// Works for Promise<T> but not Promise<Promise<T>>

// After: Awaited handles both cases cleanly
type FetchedUser = Awaited<ReturnType<typeof fetchUser>>;

// Awaited recursively unwraps:
type A = Awaited<Promise<string>>;            // string
type B = Awaited<Promise<Promise<string>>>;  // string
type C = Awaited<string>;                      // string (no-op on non-Promise)

The combination Awaited<ReturnType<typeof fn>> is worth memorizing as a unit — it covers 90% of async type extraction in app code.

Cheatsheet

Utility typeプレビューカードに表示される見出しUse frequencyPrimary use case
Partial<T>All properties become optional毎週PATCH/update payloads
Required<T>All optional properties become required月次Post-validation “this is complete” signal
Pick<T, K>Keeps only named properties毎週Public API shapes, view models
Omit<T, K>Removes named properties毎週Stripping sensitive or irrelevant fields
Record<K, V>Object with keys of type K, values of type V毎週Typed dictionaries with exhaustiveness checking
ReturnType<F>Extracts function’s return typeWeekly (especially with libraries)Type a variable from an inferred or unexported return type
Parameters<F>Extracts function’s parameter types as a tupleOccasionalWrapper functions, typed mocks
Awaited<T>Recursively unwraps Promise typesOccasionalGetting resolved type from async functions

The ones not on this list

TypeScript also ships Readonly<T>, NonNullable<T>, Extract<T,U>, Exclude<T,U>, InstanceType<C>, ConstructorParameters<C>, and several template literal types. They’re real and occasionally useful, but they come up in library code, generic utilities, and type-level metaprogramming — not in typical application logic. If you’ve memorized the eight above, look up the rest when you actually need them.

The underlying principle is the same across all of them: describe a transformation of an existing type rather than duplicating it. The specific utility type names are just vocabulary for expressing that intent to TypeScript.

広告なしで楽しみたいですか? 今すぐ広告なしで

拡張機能をインストールする

お気に入りのブラウザにIOツールを追加して、すぐにアクセスし、検索を高速化します。

に追加 Chrome拡張機能 に追加 エッジ拡張 に追加 Firefox 拡張機能 に追加 Opera 拡張機能

スコアボードが到着しました!

スコアボード ゲームを追跡する楽しい方法です。すべてのデータはブラウザに保存されます。さらに多くの機能がまもなく登場します!

ニュースコーナー 技術ハイライト付き

参加する

価値ある無料ツールの提供を継続するためにご協力ください

コーヒーを買って