TypeScript / LESSON 04 / TS04

TypeScript:関数の型

1学習の目的

2基礎解説

やりたいこと書き方意味
引数と戻り値に型function f(a: number): string数値を受け取り文字列を返す
アロー関数const f = (a: number): string => …書き方が違うだけ
戻り値なしfunction f(): void何も返さない
省略可能な引数function f(a: number, b?: string)必須引数より後ろに置く
✅ 覚えるべき重要ポイント
  1. 戻り値の型は引数の丸カッコの直後に書くfunction add(a: number, b: number): number の最後の : number が戻り値の型。
  2. return を書き忘れると実行前にエラーになる。JS08で「なぜか undefined が返る」と悩んだ問題が、TypeScriptでは書いた時点で分かる。
  3. 引数の数と型が合わないと呼び出せないf(1)f(1, 2) のどちらが正しいかを、型が教えてくれる。
  4. ? を付けると省略可能な引数になる。必須の引数より後ろに置く必要があり、順序を間違えるとエラーになる。
  5. 何も返さない関数の戻り値の型は voidvoid を返す関数の結果を変数に入れようとするとエラーになるので、うっかり使うことも防げる。

現場使用例:金額計算、データの整形、入力値の検証、APIの呼び出し。関数の型は「この関数の使い方」を説明するドキュメントにもなる

// 基本形:引数と戻り値に型を付ける
function add(a: number, b: number): number {
  return a + b;
}

// アロー関数でも同じ
const multiply = (a: number, b: number): number => a * b;

// ❌ return を書き忘れるとエラー
function broken(): number {
  console.log("計算中");
}
// → A function whose declared type is neither 'undefined',
//   'void', nor 'any' must return a value.

// 何も返さない関数は void
function log(message: string): void {
  console.log(message);
}

// 省略可能な引数(必須引数より後ろに置く)
function greet(name: string, title?: string): string {
  return title ? `${title} ${name}` : name;
}
console.log(greet("田中"));        // 田中
console.log(greet("田中", "様"));  // 様 田中

// 既定値のある引数
function withTax(price: number, rate: number = 0.1): number {
  return Math.round(price * (1 + rate));
}
console.log(withTax(1000));       // 1100
console.log(withTax(1000, 0.08)); // 1080

// オブジェクトや配列も引数にできる
function format(item: { name: string; price: number }): string {
  return `${item.name}:${item.price.toLocaleString()}円`;
}

3基本ドリル(10問)

TS04-D01 2つの数値を受け取って合計を返す関数 add を、引数と戻り値の両方に型注釈を付けて定義し、add(3, 7) の結果を出力せよ。 コーディング★☆☆無料
期待される結果

出力 == 10

ヒント

function add(a: number, b: number): number { return a + b; } と書く。

模範解答
function add(a: number, b: number): number {
  return a + b;
}

console.log(add(3, 7));
解説

引数の型は名前のあと、戻り値の型は丸カッコのあと。この2箇所を書くだけで、関数の使い方が型として固定される。

TS04-D02 function f(): number { console.log(1); } のように return を書き忘れるとどうなるか。
A. undefined が返る B. 実行前にエラーになる C. 0 が返る D. 問題ない
選択★☆☆無料
期待される結果

解答 == B

ヒント

戻り値の型を number と宣言している。

模範解答
B
解説

JS08で「なぜか undefined が返る」と悩んだ問題が、書いた時点で分かる。「number を返すと宣言したのに返していない」とTypeScriptが指摘してくれる。

TS04-D03 数値を受け取って2倍にして返すアロー関数 double を型注釈付きで定義し、double(21) の結果を出力せよ。 コーディング★☆☆無料
期待される結果

出力 == 42

ヒント

const double = (n: number): number => n * 2; と書く。

模範解答
const double = (n: number): number => n * 2;

console.log(double(21));
解説

アロー関数でも型の付け方は同じ。1行で書ける場合は return と波カッコを省略できる(JS08)。

TS04-D04 何も返さない関数の戻り値の型を補ってコードを完成させよ。 穴埋め★☆☆無料
コード
function showMessage(text: string): ____ {
  console.log(text);
}

showMessage("完了しました");
期待される結果

出力 == 完了しました

ヒント

「何も返さない」を表す型。小文字で4文字。

模範解答
void
解説

void は「戻り値が無い」ことを明示する型。書かなくても推論されるが、明示すると「この関数は値を返さない」という意図が伝わる。

TS04-D05 引数を2つ取る関数を f(1) と1つだけで呼ぶとどうなるか。
A. 2つ目は undefined になる B. 実行前にエラーになる C. 0 が入る D. 問題ない
選択★☆☆無料
期待される結果

解答 == B

ヒント

引数の数も型チェックの対象になる。

模範解答
B
解説

Expected 2 arguments, but got 1 というエラーになる。JavaScriptなら undefined のまま計算されて NaN になっていた場面。

TS04-D06 税込価格を計算する関数 withTax を定義せよ。第1引数は価格(必須)、第2引数は税率(省略時は0.1)とし、四捨五入した結果を返すこと。withTax(1000) と withTax(1000, 0.08) の結果を2行で出力せよ。 コーディング★★☆無料
期待される結果

出力2行。1100 / 1080

ヒント

既定値は rate: number = 0.1 のように書く。

模範解答
function withTax(price: number, rate: number = 0.1): number {
  return Math.round(price * (1 + rate));
}

console.log(withTax(1000));
console.log(withTax(1000, 0.08));
解説

既定値を書くと型注釈は省略できるが、明示したほうが読みやすい。よく使う値を既定値にしておくと、呼び出し側が簡潔になる。

TS04-D07 省略可能な引数を定義するコードを完成させよ。 穴埋め★★☆無料
コード
function greet(name: string, title____: string): string {
  return title ? `${title} ${name}` : name;
}

console.log(greet("田中"));
期待される結果

出力 == 田中

ヒント

「あってもなくてもよい」を表す1文字の記号。

模範解答
?
解説

省略可能な引数は必須引数より後ろに置く。順序を逆にすると「必須の引数は省略可能な引数の後には置けない」というエラーになる。

TS04-D08 function f(a?: number, b: number) { … } と書くとどうなるか。
A. 問題なく動く B. エラーになる(省略可能な引数の後に必須引数は置けない) C. b も省略可能になる D. a が必須になる
選択★★☆無料
期待される結果

解答 == B

ヒント

省略可能な引数を先に書くと、呼び出し側はどう指定すればよいか。

模範解答
B
解説

もし許されたら f(5) の5がどちらの引数か分からなくなる。だから省略可能な引数は必ず後ろに置く決まりになっている。

TS04-D09 商品オブジェクト(name と price)を受け取り、「ノートPC:128,000円」の形式の文字列を返す関数 format を型注釈付きで定義し、実行結果を出力せよ。 コーディング★★☆無料
期待される結果

出力 == ノートPC:128,000円

ヒント

引数の型は { name: string; price: number } と書く(TS03)。

模範解答
function format(item: { name: string; price: number }): string {
  return `${item.name}:${item.price.toLocaleString()}円`;
}

console.log(format({ name: "ノートPC", price: 128000 }));
解説

オブジェクトを引数に取る関数は実務で頻出。型を書いておくと、呼び出し側が渡すべきプロパティが明確になる。

TS04-D10 数値の配列を受け取って合計を返す関数 sum を型注釈付きで定義し、sum([10, 20, 30]) の結果を出力せよ。 コーディング★★☆無料
期待される結果

出力 == 60

ヒント

引数の型は number[]、戻り値は number。reduce を使う(JS09)。

模範解答
function sum(nums: number[]): number {
  return nums.reduce((total, n) => total + n, 0);
}

console.log(sum([10, 20, 30]));
解説

配列を受け取り、1つの値を返すという形は集計処理の基本。reduce の中の引数の型は、配列の型から自動的に推論される。

4実践シナリオ(5問)

TS04-S01 【送料計算】価格を受け取り、5000円以上なら0、未満なら600を返す関数 shippingFee を型注釈付きで定義せよ。4800円と12000円で試し、「4,800円 → 送料600円」の形式で2行出力すること。 コーディング★★☆無料
期待される結果

出力2行が完全一致

ヒント

三項演算子で判定する。出力は for...of で2件を回すか、2回書いてもよい。

模範解答
function shippingFee(price: number): number {
  return price >= 5000 ? 0 : 600;
}

for (const price of [4800, 12000]) {
  console.log(`${price.toLocaleString()}円 → 送料${shippingFee(price)}円`);
}
解説

1つのルールを1つの関数に閉じ込める。送料の基準が変わっても、この関数だけを直せばよい(JS08と同じ考え方)。

TS04-S02 【会員価格の計算】価格と会員フラグを受け取り、会員なら10%引き(四捨五入)、非会員なら定価を返す関数 memberPrice を定義せよ。会員フラグは省略時 false とすること。memberPrice(20000, true) と memberPrice(20000) の結果を2行出力せよ。 コーディング★★☆無料
期待される結果

出力2行。18000 / 20000

ヒント

isMember: boolean = false のように既定値を付ける。

模範解答
function memberPrice(price: number, isMember: boolean = false): number {
  return isMember ? Math.round(price * 0.9) : price;
}

console.log(memberPrice(20000, true));
console.log(memberPrice(20000));
解説

既定値を「安全な側」に設定するのがコツ。会員フラグを渡し忘れても、うっかり割引されることはない。

TS04-S03 【商品リストの整形】商品オブジェクトの配列を受け取り、「ノートPC(128,000円)」の形式の文字列配列を返す関数 formatAll を型注釈付きで定義せよ。3件(ノートPC/128000、マウス/3200、キーボード/8500)で試し、カンマ区切りで1行出力すること。 コーディング★★☆無料
期待される結果

出力 == ノートPC(128,000円), マウス(3,200円), キーボード(8,500円)

ヒント

引数は { name: string; price: number }[]、戻り値は string[]。map を使う。

模範解答
function formatAll(items: { name: string; price: number }[]): string[] {
  return items.map((i) => `${i.name}(${i.price.toLocaleString()}円)`);
}

const products = [
  { name: "ノートPC", price: 128000 },
  { name: "マウス", price: 3200 },
  { name: "キーボード", price: 8500 },
];

console.log(formatAll(products).join(", "));
解説

配列を受け取り、別の型の配列を返す{...}[] から string[] へ型が変わることが、戻り値の型注釈から読み取れる。

TS04-S04 【入力検証】商品名を受け取り、空文字なら「名前が未入力です」、20文字を超えるなら「名前が長すぎます」、それ以外は「OK」を返す関数 validateName を定義せよ。3通り("ノートPC"、""、"あ"を21回繰り返した文字列)で試し、3行出力すること。 コーディング★★★無料
期待される結果

出力3行。OK / 名前が未入力です / 名前が長すぎます

ヒント

trim() で前後の空白を除いてから判定する。21文字は "あ".repeat(21) で作れる。

模範解答
function validateName(name: string): string {
  const trimmed: string = name.trim();

  if (trimmed.length === 0) {
    return "名前が未入力です";
  }

  if (trimmed.length > 20) {
    return "名前が長すぎます";
  }

  return "OK";
}

console.log(validateName("ノートPC"));
console.log(validateName(""));
console.log(validateName("あ".repeat(21)));
解説

すべての分岐で return を書く必要がある。1つでも return を忘れた経路があると、TypeScriptが「値を返さない場合がある」と指摘してくれる。

TS04-S05 【関数を引数に取る】数値の配列と変換用の関数を受け取り、変換後の配列を返す関数 applyAll を定義せよ。[1, 2, 3] に「10倍する関数」を適用した結果と、「2乗する関数」を適用した結果を2行出力すること。 コーディング★★★無料
期待される結果

出力2行。[10,20,30] / [1,4,9] の形式

ヒント

関数の型は (n: number) => number と書く。applyAll(nums: number[], fn: (n: number) => number): number[] の形。

模範解答
function applyAll(
  nums: number[],
  fn: (n: number) => number
): number[] {
  return nums.map(fn);
}

console.log(applyAll([1, 2, 3], (n) => n * 10).join(","));
console.log(applyAll([1, 2, 3], (n) => n ** 2).join(","));
解説

関数そのものにも型が付けられる(n: number) => number は「数値を受け取って数値を返す関数」という型。JS09の高階関数が、型で守られるようになった。

5仕上げ課題

TS04-FINAL 【請求書計算モジュール】請求書の計算を、役割ごとの関数に分けて実装せよ。すべての関数に引数と戻り値の型注釈を付けること。

作る関数
subtotal(price, qty) … 小計を返す
memberDiscount(amount, isMember) … 会員なら10%引きの割引額を返す(四捨五入)。isMember は省略時 false
shippingFee(amount) … 5,000円以上なら0、未満なら600を返す
withTax(amount) … 10%の消費税を加えた金額を返す(四捨五入)
yen(n) … 数値を「128,000円」の形式の文字列にして返す

処理:単価8500円 × 数量3、会員(true)で計算し、下記を出力する。

期待される出力(5行)
小計:25,500円
会員割引:-2,550円
送料:0円
消費税:2,295円
合計:25,245円
期待される結果

出力5行が完全一致

ヒント

5つの関数を定義してから、順に呼び出して計算する。小計25500 → 割引2550 → 割引後22950 → 送料0 → 税2295 → 合計25245。yen() は toLocaleString() を使う。

模範解答
function subtotal(price: number, qty: number): number {
  return price * qty;
}

function memberDiscount(amount: number, isMember: boolean = false): number {
  return isMember ? Math.round(amount * 0.1) : 0;
}

function shippingFee(amount: number): number {
  return amount >= 5000 ? 0 : 600;
}

function withTax(amount: number): number {
  return Math.round(amount * 1.1);
}

function yen(n: number): string {
  return `${n.toLocaleString()}円`;
}

const price: number = 8500;
const qty: number = 3;
const isMember: boolean = true;

const sub: number = subtotal(price, qty);
const discount: number = memberDiscount(sub, isMember);
const afterDiscount: number = sub - discount;
const shipping: number = shippingFee(afterDiscount);
const taxed: number = withTax(afterDiscount + shipping);
const tax: number = taxed - (afterDiscount + shipping);

console.log(`小計:${yen(sub)}`);
console.log(`会員割引:-${yen(discount)}`);
console.log(`送料:${yen(shipping)}`);
console.log(`消費税:${yen(tax)}`);
console.log(`合計:${yen(taxed)}`);
解説

JS08で書いたのと同じ請求書計算だが、決定的な違いがある。それぞれの関数が「何を受け取り、何を返すか」が型として宣言されているため、使い方を間違えると呼び出した時点でエラーになる

たとえば shippingFee("25500") と文字列を渡そうとすれば止められるし、const s: string = subtotal(8500, 3); と戻り値を取り違えても止められる。JavaScriptならどちらも実行できてしまい、NaN"25500円円" のような結果が請求書に印字されて初めて気づくことになる。

さらに memberDiscountisMember: boolean = false という既定値にも意味がある。渡し忘れたときに「割引しない」側に倒れるため、うっかり全員が割引されるような事故が起きない。既定値は安全な側に設定する——これは型とは別の、設計上の判断だ。

関数に型を付けることは、その関数の「取扱説明書」をコードに埋め込むことでもある。半年後の自分や他の開発者は、関数の中身を読まなくても使い方が分かる。

次章では型推論を学ぶ。実は、ここまで書いてきた型注釈の多くは省略できる——どこを書き、どこを省くかの判断を身につける。