0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TypeScriptで学ぶ型安全な開発プラクティス【その3】高度な型の使い方

Last updated at Posted at 2023-12-28

高度な型の使い方(2025年版)✨

TypeScriptの基本的な型を理解したら、次は高度な型を活用して、より複雑で型安全なコードを記述する方法を学びましょう。本記事では、ジェネリックスユニオン型とインターセクション型、そして複雑な型パターンの処理について最新情報を交えながら解説します。


1. ジェネリックス 🔄

概要

ジェネリックスは、型パラメーターを使用して関数やクラスを汎用化する仕組みです。これにより、異なるデータ型に対応しつつ型安全性を保つことができます。

基本例

以下はジェネリック関数の例です:

function identity<T>(value: T): T {
  return value;
}

const num = identity<number>(42); // 型推論でTはnumber
const str = identity<string>("hello"); // 型推論でTはstring

💡 ポイント: <T> は「型のプレースホルダー」として機能します。呼び出し時に具体的な型が決定されます。

ジェネリック制約

ジェネリックスに制約を加えることで、特定のプロパティやメソッドを持つ型のみ許可できます。

interface Lengthwise {
  length: number;
}

function logLength<T extends Lengthwise>(item: T): void {
  console.log(item.length);
}

logLength({ length: 10, name: "test" }); // OK
logLength(123); // エラー: numberにはlengthプロパティがない

応用例

ジェネリッククラスやインターフェースも作成できます:

class DataStore<T> {
  private data: T[] = [];

  add(item: T): void {
    this.data.push(item);
  }

  getAll(): T[] {
    return this.data;
  }
}

const store = new DataStore<string>();
store.add("TypeScript");
console.log(store.getAll()); // ["TypeScript"]

2. ユニオン型とインターセクション型 🔗

ユニオン型

ユニオン型は、複数の型のいずれかを受け入れることができます。|(パイプ)記号で表します。

例:ユニオン型

type ID = string | number;

function printID(id: ID): void {
  if (typeof id === "string") {
    console.log(`文字列ID: ${id}`);
  } else {
    console.log(`数値ID: ${id}`);
  }
}

printID("abc123"); // OK
printID(123); // OK

💡 ポイント: 型ガード(typeofin 演算子)を使用して安全に処理を分岐できます。


インターセクション型

インターセクション型は、複数の型を結合し、それらすべてのプロパティを持つ新しい型を作ります。&(アンパサンド)記号で表します。

例:インターセクション型

type User = { name: string };
type Admin = { permissions: string[] };

type AdminUser = User & Admin;

const admin: AdminUser = {
  name: "Alice",
  permissions: ["read", "write"],
};

💡 ポイント: インターセクション型は複雑なデータモデルを作成する際に役立ちます。


3. 複雑な型パターンの処理 🧩

パターンマッチング

TypeScriptでは、ライブラリ(例:ts-pattern)を使用してパターンマッチングによる条件分岐が可能です。これにより、複雑な条件でも簡潔で安全なコードが書けます。

例:ts-patternによるパターンマッチング

import { match } from "ts-pattern";

type Result =
  | { type: "success"; data: string }
  | { type: "error"; message: string };

const result: Result = { type: "success", data: "Hello, World!" };

const message = match(result)
  .with({ type: "success" }, (res) => `データ: ${res.data}`)
  .with({ type: "error" }, (err) => `エラー: ${err.message}`)
  .exhaustive();

console.log(message); // データ: Hello, World!

💡 ポイント: パターンマッチングでは、すべてのケースが網羅されているかチェックされるため、安全性が向上します。


条件付き型

条件付き型は、ある条件に基づいて異なる型を返す強力な機能です。

例:条件付き型

type IsString<T> = T extends string ? true : false;

type Test1 = IsString<string>; // true
type Test2 = IsString<number>; // false

💡 ポイント: 条件付き型は柔軟な型操作やユーティリティタイプの作成に役立ちます。


ユーティリティタイプ

TypeScriptには多くの組み込みユーティリティタイプがあります。以下はその一部です:

  • Partial<T>: プロパティをすべてオプショナルにする。
  • Readonly<T>: プロパティを読み取り専用にする。
  • Pick<T, K>: 特定のプロパティのみ選択。
  • Omit<T, K>: 特定のプロパティのみ除外。

例:Partial と Readonly

interface User {
  id: number;
  name: string;
}

const updateUser = (user: Partial<User>): void => {
  console.log(user);
};

const userData: Readonly<User> = { id: 1, name: "Alice" };
// userData.name = "Bob"; // エラー:読み取り専用プロパティ

まとめ

TypeScriptの高度な型機能を活用することで、以下が実現できます:

  1. ジェネリックスによる再利用性と柔軟性の向上。
  2. ユニオン型とインターセクション型による複雑なデータモデルの安全な表現。
  3. パターンマッチングや条件付き型で効率的かつ安全なロジック構築。

これらの技術を駆使して、安全性と効率性に優れたコードを書いていきましょう! 🎉

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?