【読書メモ】初めてのTypeScript - 第II部 TypeScript の機能

5章 関数

関数のパラメーター

変数と同様に、TypeScript では、型アノテーションを使って関数パラメーターの型を宣言できます。

function attack(damage: number) {
  console.log(`${damage} のダメージを与えた`)
}

型アノテーションの中で : の前に ? を追加することで、パラメーターをオプションと指定できます。オプションパラメーターは、必ず 最後 のパラメーターでなければなりません。

function announceAttack(damage: number, target?: string) {
  if (target) {
    console.log(`${target} に ${damage} のダメージを与えた`)
  } else {
    console.log(`${damage} のダメージを与えた`)
  }
}

announceAttack(10)
announceAttack(10, 'ピカチュウ')
announceAttack(10, undefined)

function announceAttack(target?: string, damage: number) {
  // Error: A required parameter cannot follow an optional parameter.
}

TypeScript の型推論は、デフォルト値を持つ関数パラメーターに対して、初期値を持つ変数の場合と同様に機能します。

function announceAttack(damage: number, target = 'ピカチュウ') {
  console.log(`${target} に ${damage} のダメージを与えた`)
}

announceAttack(10)
announceAttack(10, 'ゼニガメ')
announceAttack(10, undefined)

レストパラメーター の型は、配列の型として指定します。

function announceAttacks(damage: number, ...targets: string[]) {
  targets.forEach((target) => {
    console.log(`${target} に ${damage} のダメージを与えた`)
  })
}

announceAttacks(10, 'ピカチュウ', 'ゼニガメ', 'フシギダネ')

戻り値の型

TypeScript は洞察力の鋭い言語であり、関数の戻り値の型を推論できます。異なる値を返す複数の return 文が関数に含まれる場合、TypeScript は戻り値の型を、関数が返し得る全ての型の合併型と推論します。

function getAttackMessage(damage: number) {
  if (damage >= 20) {
    return '効果は抜群だ!'
  } else if (damage >= 10) {
    return '効果は普通だ'
  } else if (damage >= 5) {
    return '効果はいまひとつだ'
  } else {
    return undefined
  }
}

関数の戻り値の型を明示的に宣言することもできます。

function getAttackMessage(damage: number): string | undefined {
  if (damage >= 20) {
    return '効果は抜群だ!'
  } else if (damage >= 10) {
    return '効果は普通だ'
  } else if (damage >= 5) {
    return '効果はいまひとつだ'
  } else {
    return undefined
  }
}

const getAttackMessageArrow = (damage: number): string | undefined => {
  if (damage >= 20) {
    return '効果は抜群だ!'
  } else if (damage >= 10) {
    return '効果は普通だ'
  } else if (damage >= 5) {
    return '効果はいまひとつだ'
  } else {
    return undefined
  }
}

関数の型

JavaScript では、関数を値として渡すことができますので、関数の型を宣言することができます。

let attackFunction: (damage: number, target?: string) => string

type AttackFunction = (damage: number, target?: string) => string

void

void 型は、関数が何も返さないことを示します。関数の型の宣言で使われる場合、void その関数から返された値が全て無視されることを表します。

function logAttack(damage: number): void {
  console.log(`${damage} のダメージを与えた`)
}

never

関数の中には、値を返さないだけでなく、呼び出し元に全く処理を返さないものさえあります。常にエラーをスローしたり、無限ループを実行したりする関数がこれに当たります。

処理を返さない関数に対しては、never という型アノテーションを明示的に追加することで、その関数の呼び出し後のコードが一切実行されないことを表現できます。

function fail(message: string): never {
  throw new Error(message)
}

function workWithUnsafeParam(param: unknown) {
  if (typeof param !== 'string') {
    return fail('パラメーターは文字列である必要があります')
  }

  console.log(param.toUpperCase())
}

関数のオーバーロード

JavaScript の関数の中には、オプションパラメーターやレストパラメーターだけでは表現できない、大幅に異なるパラメーターのセットを使って呼び出せるものもあります。このような関数は、オーバーロードシグネチャ と呼ばれる TypeScript の構文を使って表せます。

function announceAttack(damage: number, target: string): void
function announceAttack(damage: number, targets: string[]): void
function announceAttack(
  damage: number,
  targetOrTargets: string | string[],
): void {
  if (Array.isArray(targetOrTargets)) {
    targetOrTargets.forEach((target) => {
      console.log(`${target} に ${damage} のダメージを与えた`)
    })
  } else {
    console.log(`${targetOrTargets} に ${damage} のダメージを与えた`)
  }
}

関連記事