関数の型注釈
基本的な関数
// 引数と戻り値に型を指定
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でアサーション関数を定義
次回は配列とコレクションについて学びます。