2022.04.28(更新日: 2024.08.08)
      TypeScript 学習 part7
      はじめに
こちらの本でTypeScriptの学習を進めています。

前回は、ユーザーがゲームの難易度を選択できるようにしました。今回は、その続きです。
コミット履歴はこちら
行ったこと
ユーザーが想定外の難易度を入力した時の対処
ゲームの設定では、normalとhardモードしか選ぶことが出来ません。仮にユーザーがvery hardと入力してしまった場合は、エラーが発生してしまう状態でした。
完成後の画面
まずnormalかhardを選ぶことが明示され、ユーザーがvery hardを選んでしまったら、再入力を促すようになりました。

promptSelect関数の作成
ユーザーにモードの選択肢を表示するpromptSelect関数を作成しました。赤字の関数(printLine, readLineについて、この後解説します)
const promptSelect = async (text: string, values: readonly string[]): Promise<string> => {
  printLine(`\n${text}`)
  values.forEach((value) => {
    printLine(`- ${value}`)
  })
  printLine(`> `, false)
  const input = await readLine()
  if (values.includes(input)) {
    return input
  } else {
    return promptSelect(text, values)
  }
}
printLine関数はこちらです。
const printLine = (text: string, breakLine: boolean = true) => {
  process.stdout.write(text + (breakLine ? '\n' : ''))
}
また、readLine関数はこちらです。
const readLine = async () => {
  const input: string = await new Promise((resolve) =>
                          process.stdin.once('data', (data) => resolve(data.toString())))
  return input.trim()
}
promptSelect関数は、settingメソッド内で使用しました。
  async setting() {
    this.mode = await promptSelect('モードを入力してください。', ['normal', 'hard']) as Mode
    const answerLength = this.getAnswerLength()
    while (this.answer.length < answerLength) {
      const randNum = Math.floor(Math.random() * this.answerSource.length)
      const selectedItem = this.answerSource[randNum]
      if (!this.answer.includes(selectedItem)) {
        this.answer.push(selectedItem)
      }
    }
  }
ジェネリクスの使用
型を動的に扱えるジェネリクスを使用しました。
const promptSelect = async <T extends string>(text: string, values: readonly string[]): Promise<T> => {
  printLine(`\n${text}`)
  values.forEach((value) => {
    printLine(`- ${value}`)
  })
  printLine(`> `, false)
  const input = (await readLine()) as T
  if (values.includes(input)) {
    return input
  } else {
    return promptSelect<T>(text, values)
  }
}
  async setting() {
    this.mode = await promptSelect<Mode>('モードを入力してください。', ['normal', 'hard'])
    const answerLength = this.getAnswerLength()
    while (this.answer.length < answerLength) {
      const randNum = Math.floor(Math.random() * this.answerSource.length)
      const selectedItem = this.answerSource[randNum]
      if (!this.answer.includes(selectedItem)) {
        this.answer.push(selectedItem)
      }
    }
  }
DRYなコードにした
DRY(Don’t repeat yourself)なコードにしました。(繰り返しを避けたコード)
const modes = ['normal', 'hard'] as const
type Mode = typeof modes[number]
class HitAndBlow {
  private readonly answerSource = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
  private answer: string[] = []
  private tryCount = 0
  private mode: Mode = 'normal'
  async setting() {
    this.mode = await promptSelect<Mode>('モードを入力してください。', modes)
    const answerLength = this.getAnswerLength()
    while (this.answer.length < answerLength) {
      const randNum = Math.floor(Math.random() * this.answerSource.length)
      const selectedItem = this.answerSource[randNum]
      if (!this.answer.includes(selectedItem)) {
        this.answer.push(selectedItem)
      }
    }
  }
// 以下省略
次回
次回は、新しいブランチを切って、hit and blow以外も遊べるようなゲームにしていきます。
コメントを残す