TypeScript PR

【TypeScript入門】keyof型演算子(type operator)の使い方をコード例で徹底解説

記事内に商品プロモーションを含む場合があります

こんにちは!

TypeScriptにおける「keyof型演算子」について、詳しく知りたくありませんか?オブジェクトの型からプロパティ名を取得する、この強力な機能の活用法をマスターしたい方も多いのではないでしょうか。

keyof演算子って何だろう?どうやって使うの?
型安全性を高めるって聞いたけど具体的にはどういうこと?
typeofと組み合わせて使う方法も知りたい!

TypeScriptでの型安全な開発において、このような疑問をお持ちではないでしょうか。

この記事では、TypeScriptの「keyof型演算子」について、基本概念から実践的な使い方、応用テクニックまでを詳しく解説します。

この記事は次のような方におすすめです。

この記事はこんな人におすすめ!
  • TypeScriptのkeyof演算子について学びたい方
  • 型安全なオブジェクト操作を実装したい開発者
  • Mapped Typesとの組み合わせ方を知りたい方
  • TypeScriptの型システムをより深く理解したい方
  • 実践的なコード例で学習したい方

この記事を読めば、keyof演算子の仕組みが理解でき、実際のプロジェクトですぐに活用できるスキルが身につきます。TypeScriptの型システムを最大限に活用して、より安全で保守性の高いコードを書けるようになりましょう。

「型安全なコードを書きたい方」「TypeScriptの型演算子を深く学びたい方」は、ぜひ参考にしてください。

それでは、順を追って詳しく見ていきましょう!

keyof型演算子とは何か

keyof型演算子は、オブジェクト型からプロパティ名を型として取得するTypeScriptの型演算子です。対象となるオブジェクト型のすべてのプロパティ名を、ユニオン型として返します。

keyof演算子の基本的な特徴は以下の通りです。

  • オブジェクト型のプロパティ名をすべて抽出します
  • 結果はプロパティ名のユニオン型として返されます(文字列プロパティは文字列リテラル型、数値プロパティは数値リテラル型、シンボルプロパティはsymbol型)
  • コンパイル時に型チェックが行われるため、タイプミスを防げます
  • Mapped Typesと組み合わせることで、強力な型変換が可能になります

keyof演算子は、TypeScriptの型システムにおいて重要な役割を果たしており、型安全性を保ちながら柔軟なコード作成を可能にします。

keyof演算子の基本的な使い方

keyof演算子の基本的な構文は非常にシンプルです。オブジェクト型の前に keyof キーワードを付けるだけで使用できます。

type Person = {
  name: string;
  age: number;
  email: string;
};

// Personオブジェクトのプロパティ名を取得
type PersonKeys = keyof Person;
// PersonKeys は "name" | "age" | "email" となる

// 使用例
let property: PersonKeys = "name";    // OK
property = "age";                     // OK
property = "email";                   // OK
property = "address";                 // エラー: Type '"address"' is not assignable to type '"name" | "age" | "email"'

この例では、PersonKeys型は"name" | "age" | "email"というユニオン型になります。存在しないプロパティ名を指定しようとすると、コンパイル時にエラーが発生するため、安全にプロパティ名を扱えます。

複数のプロパティを持つオブジェクトでの使用

より複雑なオブジェクト型でも、keyof演算子は同様に動作します。

type Product = {
  id: number;
  name: string;
  price: number;
  category: string;
  inStock: boolean;
};

type ProductKeys = keyof Product;
// "id" | "name" | "price" | "category" | "inStock"

function getProductProperty(product: Product, key: ProductKeys) {
  return product[key];
}

const myProduct: Product = {
  id: 1,
  name: "ノートPC",
  price: 80000,
  category: "electronics",
  inStock: true
};

// 型安全にプロパティにアクセス
const productName = getProductProperty(myProduct, "name");     // string
const productPrice = getProductProperty(myProduct, "price");   // number
const invalidProp = getProductProperty(myProduct, "weight");   // エラー

この例では、getProductProperty関数が型安全にオブジェクトのプロパティにアクセスできます。存在しないプロパティ名を指定するとコンパイル時にエラーが発生するため、実行時エラーを防げます。

keyofとtypeof演算子の組み合わせ

keyof演算子は、typeof演算子と組み合わせることで、実際の値からプロパティ名を動的に取得できます。この組み合わせは非常に強力で、実際の開発でよく使用されます。

const user = {
  id: 1,
  username: "taro",
  email: "taro@example.com",
  isActive: true
};

// userオブジェクトの型を取得してからkeyofを適用
type UserKeys = keyof typeof user;
// "id" | "username" | "email" | "isActive"

function getUserProperty(obj: typeof user, key: UserKeys) {
  return obj[key];
}

// 型安全にプロパティにアクセス可能
const userId = getUserProperty(user, "id");           // number
const userEmail = getUserProperty(user, "email");     // string

この方法では、既存のオブジェクトから型情報を抽出し、その型に基づいてkeyof演算子を適用できます。型定義を別途作成する必要がなく、実際の値と型定義の同期を保てるメリットがあります。

インデックス型でのkeyof演算子

keyof演算子をインデックス型に適用すると、インデックスキーの型が返されます。インデックス型の種類によって、返される型が異なります。

// 文字列インデックス型
type StringIndexType = {
  [key: string]: any;
};
type StringKeys = keyof StringIndexType;
// string | number (numberはstringに変換可能なため含まれる)

// 数値インデックス型
type NumberIndexType = {
  [key: number]: any;
};
type NumberKeys = keyof NumberIndexType;
// number

// 具体的なキーを持つインデックス型
type SpecificKeys = {
  [K in "x" | "y" | "z"]: any;
};
type SpecificKeyType = keyof SpecificKeys;
// "x" | "y" | "z"

この例が示すように、インデックス型の定義方法によって、keyof演算子の結果が変わります。特に文字列インデックス型では、JavaScriptの仕様により数値キーも文字列として扱えるため、string | number型が返されます。

Mapped Typesとの組み合わせ

keyof演算子の真価は、Mapped Typesと組み合わせたときに発揮されます。Mapped Typesを使用することで、既存の型から新しい型を生成できます。

type User = {
  name: string;
  age: number;
  email: string;
};

// すべてのプロパティをオプショナルにする
type PartialUser = {
  [K in keyof User]?: User[K];
};
// { name?: string; age?: number; email?: string; }

// すべてのプロパティをreadonlyにする
type ReadonlyUser = {
  readonly [K in keyof User]: User[K];
};
// { readonly name: string; readonly age: number; readonly email: string; }

// 使用例
const partialUser: PartialUser = {
  name: "太郎"  // ageとemailは省略可能
};

const readonlyUser: ReadonlyUser = {
  name: "花子",
  age: 25,
  email: "hanako@example.com"
};
// readonlyUser.name = "変更"; // エラー: Cannot assign to 'name' because it is a read-only property

このようにMapped Typesと組み合わせることで、既存の型を基に新しい型を動的に生成できます。TypeScriptには、このパターンを活用した標準的なユーティリティ型(Partial<T>Readonly<T>など)が用意されています。

keyof演算子の特殊なケース

keyof演算子を使用する際に知っておくべき特殊なケースがいくつかあります。

空のオブジェクト型

プロパティを持たないオブジェクト型にkeyof演算子を適用すると、never型が返されます。

type EmptyObject = {};
type EmptyKeys = keyof EmptyObject;
// never型

// never型は値を代入できない
let key: EmptyKeys;
// key = "anything"; // エラー: Type '"anything"' is not assignable to type 'never'

any型でのkeyof演算子

any型にkeyof演算子を適用すると、オブジェクトのキーとして使用可能なすべての型のユニオン型が返されます。

type AnyKeys = keyof any;
// string | number | symbol

// JavaScriptでオブジェクトのキーとして使用可能なすべての型
let anyKey: AnyKeys = "string";     // OK
anyKey = 123;                       // OK
anyKey = Symbol("sym");             // OK

配列型でのkeyof演算子

配列型にkeyof演算子を適用すると、配列が持つメソッド名やプロパティ名がすべて返されます。

type ArrayKeys = keyof string[];
// "length" | "push" | "pop" | "shift" | "unshift" | "indexOf" | "slice" | ...

const arr: string[] = ["a", "b", "c"];
function getArrayProperty(array: string[], key: ArrayKeys) {
  return array[key];
}

const length = getArrayProperty(arr, "length");    // number
const pushMethod = getArrayProperty(arr, "push");  // (...items: string[]) => number

実践的なkeyof演算子の活用例

keyof演算子を使った実践的な例をいくつか紹介します。これらの例は、実際の開発で役立つパターンです。

オブジェクトのプロパティをチェックする関数

オプショナルなプロパティを持つオブジェクトで、特定のプロパティが存在するかどうかを型安全にチェックする関数を作成できます。この関数は型ガードとして機能し、プロパティの存在確認後に型の絞り込みを行います。

function hasProperty<T extends object, K extends keyof T>(
  obj: T,
  key: K
): obj is T & Record<K, NonNullable<T[K]>> {
  return obj[key] !== undefined && obj[key] !== null;
}

type Config = {
  apiUrl?: string;
  timeout?: number;
  retries?: number;
};

const config: Config = {
  apiUrl: "https://api.example.com",
  timeout: 5000
};

// 型ガードとして使用
if (hasProperty(config, "apiUrl")) {
  // この時点でconfig.apiUrlはstring型として認識される
  console.log(config.apiUrl.toUpperCase());
}

if (hasProperty(config, "retries")) {
  // retriesは未定義なのでfalse
  console.log("Retries:", config.retries);
}

この関数の型定義を詳しく解説します。

  • T extends object。第1型パラメータTはオブジェクト型に限定されます
  • K extends keyof T。第2型パラメータKはオブジェクトTのプロパティ名に限定されます
  • 戻り値の型obj is T & Record<K, NonNullable<T[K]>>は型述語(type predicate)で、この関数がtrueを返した場合にobjの型を絞り込みます

Record<K, NonNullable<T[K]>>は、キーKに対して非null・非undefinedな値を持つオブジェクト型を表します。これにより、関数がtrueを返した後は、該当プロパティが確実に存在することがTypeScriptに認識されます。

型安全なオブジェクト更新関数

オブジェクトの特定のプロパティを型安全に更新する汎用的な関数を作成できます。この関数は、存在しないプロパティへの代入や、型の異なる値の代入をコンパイル時に防ぎます。

function updateObject<T, K extends keyof T>(
  obj: T,
  key: K,
  value: T[K]
): T {
  return {
    ...obj,
    [key]: value
  };
}

type Settings = {
  theme: "light" | "dark";
  fontSize: number;
  autoSave: boolean;
};

const settings: Settings = {
  theme: "light",
  fontSize: 14,
  autoSave: true
};

// 型安全な更新
const newSettings = updateObject(settings, "theme", "dark");        // OK
const newSettings2 = updateObject(settings, "fontSize", 16);        // OK
const invalidUpdate = updateObject(settings, "theme", "invalid");   // エラー

この関数の仕組みを詳しく解説します。

型パラメータの役割
  • T。更新対象のオブジェクトの型を表します
  • K extends keyof T。オブジェクトTに存在するプロパティ名のみを受け取るよう制約します
型安全性の実現方法
  • key: Kにより、存在しないプロパティ名を指定するとコンパイルエラーになります
  • value: T[K]により、指定されたプロパティの型と異なる値を渡すとコンパイルエラーになります
  • T[K]はインデックスアクセス型で、プロパティKの型を取得します
関数の動作

スプレッド構文(...obj)で元のオブジェクトを展開し、computed property name([key]: value)で指定されたプロパティを上書きします。この方法により、元のオブジェクトを変更せずに新しいオブジェクトを返すイミュータブルな更新が実現されます。

この例では、updateObject関数が型安全にオブジェクトのプロパティを更新できます。不正な値を設定しようとするとコンパイル時にエラーが発生するため、実行時エラーを防げます。

keyof演算子のメリットと注意点

keyof演算子を使用することで得られるメリットと、使用時の注意点について説明します。

メリット

keyof演算子を使用することで、以下のようなメリットがあります。

メリット 説明
型安全性の向上 プロパティ名のタイプミスをコンパイル時に検出できます
保守性の向上 オブジェクト型が変更されても、関連する型は自動的に更新されます
コードの自動補完 IDEがプロパティ名を自動補完してくれるため、開発効率が向上します
リファクタリングの安全性 プロパティ名を変更した際に、影響範囲を正確に把握できます

注意点

keyof演算子を使用する際の注意点もあります。

  • 型レベルでの操作であるため、実行時には情報が失われます
  • 複雑な型操作は、コンパイル時間に影響を与える可能性があります
  • 過度に複雑な型定義は、可読性を損なう場合があります
  • TypeScriptのバージョンによって、一部の動作が異なる場合があります

keyofと他の型演算子との比較

TypeScriptには、keyof以外にも様々な型演算子があります。それぞれの用途と特徴を比較してみましょう。

演算子 用途 返される型
keyof オブジェクトのプロパティ名を取得 プロパティ名のユニオン型
typeof 値から型を取得 値の型
in Mapped Typesでプロパティを列挙 新しいオブジェクト型

これらの型演算子を組み合わせることで、より複雑で柔軟な型操作が可能になります。

// 複数の演算子を組み合わせた例
const data = {
  users: [{ id: 1, name: "太郎" }],
  products: [{ id: 1, title: "商品A" }]
};

type DataKeys = keyof typeof data;           // "users" | "products"
type DataValues = (typeof data)[DataKeys];   // { id: number; name: string; }[] | { id: number; title: string; }[]

// 条件付き型と組み合わせ
type ArrayElementType<T> = T extends (infer U)[] ? U : never;
type UserType = ArrayElementType<(typeof data)["users"]>;  // { id: number; name: string; }

【付録】さらに学びを深めるためのリソース


さらにTypescriptの学習を進めたい方のために、いくつかのリソースを紹介します。
これらのリソースを活用することで、TypeScriptの型システムについてより深い知識を得ることができるでしょう。

おすすめの書籍


プロを目指す人のためのTypeScript入門


プロ開発者への近道!基礎から応用まで幅広くカバー

1一番のおすすめは「プロを目指す人のためのTypeScript入門」です。
この本は、TypeScriptの基礎から高度な使い方まで幅広く解説しています。

初心者でも理解しやすい説明から始まり、徐々に難易度を上げていく構成が特徴。
プログラミング経験者なら、さらに深い理解が得られるでしょう。

実際の開発現場で使えるテクニックも豊富に紹介されているのがポイント。
型の使い方や設計パターンなど、実践的な内容が満載です。

プロの開発者を目指す人はもちろん、TypeScriptをしっかり学びたい人におすすめの一冊。
基礎固めから応用力の向上まで、幅広いニーズに応えてくれます。

現場で使えるTypeScript 詳解実践ガイド


実践的スキルを身につけたい人必見!現場のノウハウが詰まった一冊

次におすすめは「現場で使えるTypeScript 詳解実践ガイド」がランクイン。
この本の特徴は、実際の開発現場で役立つ知識が詰まっていること。

TypeScriptの基本的な文法から始まり、実際のプロジェクトでどう活用するかまで丁寧に解説しています。
エラー対処法や性能改善のコツなど、現場ならではの知恵も満載。

特に、大規模なプロジェクトでTypeScriptを使う際のベストプラクティスが学べるのが魅力。
コードの保守性や再利用性を高める方法も詳しく紹介されています。

すでにJavaScriptの経験がある人や、実務でTypeScriptを使いたい人におすすめ。
この本を読めば、現場で即戦力として活躍できる力が身につくはずです。

書籍に関してはこちらの記事も参考にしてくださいね!




オンラインで参照できる公式ドキュメント


TypeScript公式ハンドブック


https://www.typescriptlang.org/docs/
TypeScriptの公式ドキュメントです。
intersection型を含む、すべての型システムの機能について詳細な説明があります。

TypeScript Deep Dive


https://basarat.gitbook.io/typescript/
TypeScriptの深い部分まで掘り下げて解説しているオンラインブックです。
無料で読むことができ、intersection型についても詳しく説明されています。

TypeScriptの学習は終わりがありません。
新しい機能が常に追加され、より良い書き方が発見されています。
継続的に学習を続けることで、より良いTypeScriptプログラマーになれるはずです。

キャリア形成/給与還元
ひとつひとつ真摯に向き合う企業
ONE_WEDGE社員募集

株式会社 ONE WEDGEでは、新たな仲間を募集しています!

私たちと一緒に、革新的で充実したキャリアを築きませんか?
当社は、従業員が仕事と私生活のバランスを大切にできるよう、充実した福利厚生を整えています。

  • 完全週休2日制(土日休み)で、祝日や夏季休暇、年末年始休暇もしっかり保証!
  • 様々な休暇制度(有給、慶弔、産前・産後、育児、バースデー休暇、有給6日取得で特別休暇付与)を完備!
  • 従業員の成長と健康を支援するための表彰制度、資格取得支援、健康促進手当など!
  • 生活を支えるテレワーク手当、記事寄稿手当、結婚祝金・出産祝金など、様々な手当を提供!
  • 自己啓発としての書籍購入制度や、メンバー間のコミュニケーションを深める交流費補助!
  • 成果に応じた決算賞与や、リファラル採用手当、AI手当など、頑張りをしっかり評価!
  • ワークライフバランスを重視し、副業もOK!

株式会社 ONE WEDGEでは、一人ひとりの従業員が自己実現できる環境を大切にしています。
共に成長し、刺激を与え合える仲間をお待ちしております。
あなたの能力と熱意を、ぜひ当社で発揮してください。
ご応募お待ちしております!

ホームページ、採用情報は下記ボタンからご確認ください!

応募、ご質問など、お問い合わせフォーム、またはX (旧Twitter)、InstagramのDMでお気軽にご相談ください♪

まとめ

TypeScriptのkeyof型演算子は、オブジェクト型からプロパティ名を安全に取得するための強力なツールです。型安全性を保ちながら、柔軟なコード作成を可能にします。

keyof演算子の主要な特徴とメリットは以下の通りです。

  • オブジェクト型のプロパティ名をユニオン型として取得できます
  • コンパイル時の型チェックにより、プロパティ名のタイプミスを防げます
  • typeof演算子と組み合わせることで、実際の値から型情報を抽出できます
  • Mapped Typesと組み合わせることで、既存の型から新しい型を生成できます
  • 保守性が向上し、リファクタリング時の安全性が高まります

適切にkeyof演算子を活用することで、TypeScriptの型システムの恩恵を最大限に受けられます。型安全で保守性の高いコードを書くために、ぜひkeyof演算子をマスターしてください。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です