Open4

手を動かしながら学ぶ TypeScriptをやる

cryptoboxcryptobox

環境構築

package.jsonの準備

npm init -y
npm install -D typescript@4.3.5

「.ts」ファイルのコンパイル

mkdir src
touch src/index.ts

package.jsonの追記

{
  "name": "handson-typescript",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "tsc",  // 追記
    "dev": "tsc -w"  // 追記
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "typescript": "^4.3.5"
  }
}

tsconfig.jsonの追記

{
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  }
}

このタイミングで npm run dev をしてdistディレクトリ内にindex.jsが追加されることを確認

strict trueを追記

{
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src",
		"strict": true
  }
}

実行コマンドの追記

{
  "name": "handson-typescript",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "tsc",
    "dev": "tsc -w",
    "start": "node dist/index.js" // 追記
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "typescript": "^4.3.5"
  }
}

npm start で書いたコードを実行!!

cryptoboxcryptobox

Node.js環境用のセットアップ

src/index.ts を書き換える

const sayHello = (name: string) => {
  return `Hello, ${name}`
}

pocess.stdout.write(sayHello('Michael Jackson'));
src/index.ts:5:1 - error TS2304: Cannot find name 'pocess'.

上記のようなエラーが出る。
これNode.jsの機能に関する型定義ファイルを用意していないから

下記コマンドで型定義ファイルをプロジェクトに追加する
ちなみに型定義ファイルはアプリケーションの実行時には使用しないので -D オプションをつけている。
これは --save-dev の省略形

npm install -D @types/node@16.4.13

これでエラーが出なくなる

cryptoboxcryptobox

対話用の関数を書いてみよう

printLine関数の追加

まずは受け取った値を出力するだけの関数を書く
src/index.ts を以下のように書き換える

src/index.ts
const printLine = (text: string, breakLine: boolean = true) => {
  process.stdout.write(text + (breakLine ? '\n' : ''))
}

promptInput関数の追加

次にユーザーに質問を投げかけ入力してもらうための関数を追加する

src/index.ts
const printLine = (text: string, breakLine: boolean = true) => {
const promptInput = async (text: string) => {
  printLine(`\n${text}\n>`, false);
  const input: string = await new Promise((resolve) =>
    process.stdin.once("data", (data) => resolve(data.toString()))
  );
  return input.trim();
};
  • 非同期処理でユーザーからの入力を待つ
  • process.stdin.once はデータを一度だけ受け取る
  • 'data'イベントを登録することでユーザーがターミナルでエンターを押したタイミングで入力された文字列を読み取る
  • input.trim() で入力された改行コードを取り除いている。process.stdin.once で読み込んだデータは改行が含まれる為

動作確認

下記コードを追記して動作確認する

src/index.ts
(async () => {
  const name = await promptInput('名前を入力してください')
  console.log(name);
  const age = await promptInput('年齢を入力してください')
  console.log(age);
  process.exit()
})()
cryptoboxcryptobox

ゲームの処理を書いてみよう

「HitAndBlow」クラスの作成

すべての処理を手続き型で書くとコードの見通しが悪くなってしまうらしい。だからHitAndBlowクラスを作ってオブジェクト指向的にコードを書いていくらしい。

src/index.ts
class HintAndBlow {
  answerSource: string[]
  answer: string[]
  tryCount: number

  constructor() {
    this.answerSource = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
    this.answer = []
    this.tryCount = 0
  }
}

constructor内ではpropertyの初期化を行っている

インスタンスの作成

挙動を確かめるためHintAndBlowクラスのインスタンスを作ってみる。

src/index.ts
(async () => {
-  const name = await promptInput('名前を入力してください')
-  console.log(name);
-  const age = await promptInput('年齢を入力してください')
-  console.log(age);
-  process.exit()
+  const hintAndBlow = new HintAndBlow()
})()

クラスと型

VScodeで変数hintAndBlowにカーソルを合わせるとhintAndBlowはHintAndBlow型であることがわかる。TypeScriptでは宣言されたクラスを型情報として扱えるらしい。だから下記のように型アノテーションとしてもクラスを使える

const geme: HintAndBlow = new HintAndBlow()

まあクラスを型にできるゆーてもinterfaceで定義するのとほぼ変わらんらしい
以下とほぼ一緒

interface HintAndBlowInterface {
  answerSource: string[]
  answer: string[]
  tryCount: number
}

##「 HitAndBlow」クラスのリファクタリング
さっき HitAndBlow」クラス作ったけど実はもっと短くかける

class HintAndBlow {
  answerSource = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
  answer: string[] = []
  tryCount = 0
}