チュートリアル

TypeScript基礎:関数と型

TypeScript入門関数型システム
広告エリア

関数の型注釈

基本的な関数

// 引数と戻り値に型を指定
function add(a: number, b: number): number {
  return a + b;
}

// 戻り値がない場合はvoid
function greet(name: string): void {
  console.log(`こんにちは、${name}さん`);
}

// 戻り値の型推論
function multiply(a: number, b: number) {
  return a * b;  // number型と推論される
}

アロー関数

// 基本形
const add = (a: number, b: number): number => {
  return a + b;
};

// 省略形
const double = (n: number): number => n * 2;

// 型注釈を変数に付ける
const greet: (name: string) => void = (name) => {
  console.log(`こんにちは、${name}さん`);
};

関数型

関数型の定義

// 関数型
type MathFunc = (a: number, b: number) => number;

const add: MathFunc = (a, b) => a + b;
const subtract: MathFunc = (a, b) => a - b;

// インターフェースでも定義可能
interface StringProcessor {
  (input: string): string;
}

const toUpper: StringProcessor = (input) => input.toUpperCase();

コールバック関数

function processArray(
  arr: number[],
  callback: (item: number, index: number) => number
): number[] {
  return arr.map(callback);
}

const doubled = processArray([1, 2, 3], (item) => item * 2);

オプション引数とデフォルト値

オプション引数

function greet(name: string, greeting?: string): string {
  if (greeting) {
    return `${greeting}, ${name}さん`;
  }
  return `こんにちは、${name}さん`;
}

greet("太郎");              // "こんにちは、太郎さん"
greet("太郎", "おはよう");  // "おはよう、太郎さん"

デフォルト値

function greet(name: string, greeting: string = "こんにちは"): string {
  return `${greeting}, ${name}さん`;
}

greet("太郎");              // "こんにちは、太郎さん"
greet("太郎", "おはよう");  // "おはよう、太郎さん"

残余引数(Rest Parameters)

function sum(...numbers: number[]): number {
  return numbers.reduce((acc, n) => acc + n, 0);
}

sum(1, 2, 3);        // 6
sum(1, 2, 3, 4, 5);  // 15

// 固定引数との組み合わせ
function log(prefix: string, ...messages: string[]): void {
  console.log(prefix, ...messages);
}

関数オーバーロード

同じ関数名で異なる引数パターンを定義します。

// オーバーロードシグネチャ
function format(value: string): string;
function format(value: number): string;
function format(value: Date): string;

// 実装
function format(value: string | number | Date): string {
  if (typeof value === "string") {
    return value.trim();
  } else if (typeof value === "number") {
    return value.toFixed(2);
  } else {
    return value.toISOString();
  }
}

format("hello ");         // "hello"
format(3.14159);          // "3.14"
format(new Date());       // ISO形式の日付文字列

より複雑なオーバーロード

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

// オーバーロード
function getUser(id: number): User;
function getUser(name: string): User[];
function getUser(idOrName: number | string): User | User[] {
  if (typeof idOrName === "number") {
    // IDで1件取得
    return { id: idOrName, name: "User" };
  } else {
    // 名前で検索
    return [{ id: 1, name: idOrName }];
  }
}

const user = getUser(1);        // User型
const users = getUser("太郎");  // User[]型

ジェネリック関数

基本

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

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

制約付きジェネリクス

// lengthプロパティを持つ型に制限
function getLength<T extends { length: number }>(arg: T): number {
  return arg.length;
}

getLength("hello");     // 5
getLength([1, 2, 3]);   // 3
// getLength(123);      // エラー:lengthプロパティがない

// keyof制約
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: "太郎", age: 25 };
getProperty(user, "name");  // string型
getProperty(user, "age");   // number型
// getProperty(user, "foo"); // エラー

複数の型パラメータ

function map<T, U>(arr: T[], fn: (item: T) => U): U[] {
  return arr.map(fn);
}

const numbers = [1, 2, 3];
const strings = map(numbers, (n) => n.toString());  // string[]

this の型

interface User {
  name: string;
  greet(this: User): string;
}

const user: User = {
  name: "太郎",
  greet() {
    return `こんにちは、${this.name}です`;
  }
};

// thisを明示的に型付け
function greet(this: { name: string }): string {
  return `こんにちは、${this.name}です`;
}

アサーション関数

条件を満たさない場合に例外を投げる関数です。

function assertIsDefined<T>(value: T): asserts value is NonNullable<T> {
  if (value === undefined || value === null) {
    throw new Error("値がnullまたはundefinedです");
  }
}

function processUser(user: { name: string } | null): void {
  assertIsDefined(user);
  // この時点でuserはnullではないと推論される
  console.log(user.name);
}

高階関数

// 関数を返す関数
function createMultiplier(factor: number): (n: number) => number {
  return (n) => n * factor;
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

double(5);  // 10
triple(5);  // 15

// 関数を受け取って関数を返す
function compose<A, B, C>(
  f: (b: B) => C,
  g: (a: A) => B
): (a: A) => C {
  return (a) => f(g(a));
}

const addOne = (n: number) => n + 1;
const double2 = (n: number) => n * 2;
const addOneThenDouble = compose(double2, addOne);

addOneThenDouble(5);  // 12

実践例:APIクライアント

interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

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

interface Post {
  id: number;
  title: string;
  body: string;
}

// ジェネリック関数でAPI呼び出し
async function fetchApi<T>(endpoint: string): Promise<ApiResponse<T>> {
  const response = await fetch(`https://api.example.com${endpoint}`);
  const data = await response.json();
  return {
    data,
    status: response.status,
    message: response.ok ? "success" : "error"
  };
}

// 型安全なAPI呼び出し
async function getUser(id: number): Promise<User> {
  const response = await fetchApi<User>(`/users/${id}`);
  return response.data;
}

async function getPosts(): Promise<Post[]> {
  const response = await fetchApi<Post[]>("/posts");
  return response.data;
}

// 使用例
async function main(): Promise<void> {
  const user = await getUser(1);      // User型
  const posts = await getPosts();     // Post[]型
  console.log(user.name, posts.length);
}

まとめ

  • 関数の引数と戻り値に型を指定
  • ?でオプション引数、=でデフォルト値
  • オーバーロードで複数のシグネチャを定義
  • ジェネリクスで再利用可能な型安全な関数
  • extendsで型パラメータに制約を追加
  • assertsでアサーション関数を定義

次回は配列とコレクションについて学びます。

広告エリア