TypeScriptで使いまわしやすいコードを書く
Node.js環境上でCSVファイルを取り込んで何かしらの加工をして吐き出すプログラムを書きながら、使い回しのし易いTypeScriptの書き方を学ぶ。
セットアップ
Packages
yarn add -D @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser concurrently eslint eslint-config-prettier eslint-config-standard-with-typescript eslint-plugin-import eslint-plugin-node eslint-plugin-promise eslint-plugin-standard nodemon prettier rimraf typescript
npm scripts
"scripts": { "start:build": "tsc -w", "start:run": "nodemon build/index.js", "start": "concurrently yarn:start:*", "clean": "rimraf build" },
CSVファイル
一例としてサッカーの試合結果一覧のCSVデータを利用する。
football.csv
10/08/2018,Man United,Leicester,2,1,H,A Marriner 11/08/2018,Bournemouth,Cardiff,2,0,H,K Friend 11/08/2018,Fulham,Crystal Palace,0,2,A,M Dean 11/08/2018,Huddersfield,Chelsea,0,3,A,C Kavanagh 11/08/2018,Newcastle,Tottenham,1,2,A,M Atkinson 11/08/2018,Watford,Brighton,2,0,H,J Moss 11/08/2018,Wolves,Everton,2,2,D,C Pawson 12/08/2018,Arsenal,Man City,0,2,A,M Oliver 12/08/2018,Liverpool,West Ham,4,0,H,A Taylor 12/08/2018,Southampton,Burnley,0,0,D,G Scott 18/08/2018,Cardiff,Newcastle,0,0,D,C Pawson 18/08/2018,Chelsea,Arsenal,3,2,H,M Atkinson 18/08/2018,Everton,Southampton,2,1,H,L Mason 18/08/2018,Leicester,Wolves,2,0,H,M Dean 18/08/2018,Tottenham,Fulham,3,1,H,A Taylor 18/08/2018,West Ham,Bournemouth,1,2,A,S Attwell 19/08/2018,Brighton,Man United,3,2,H,K Friend 19/08/2018,Burnley,Watford,1,3,A,P Tierney 19/08/2018,Man City,Huddersfield,6,1,H,A Marriner 20/08/2018,Crystal Palace,Liverpool,0,2,A,M Oliver
最初のコード
マンUの試合結果を取得してくるロジックをハードコードしたもの。
src/index.ts
import * as fs from 'fs' const matches = fs .readFileSync('football.csv', { encoding: 'utf-8', }) .split('\n') .map((row: string): string[] => row.split(',')) let manUnitedWins = 0 for (const match of matches) { if (match[1] === 'Man United' && match[5] === 'H') { manUnitedWins++ } else if (match[2] === 'Man United' && match[5] === 'A') { manUnitedWins++ } } console.log(`Man United won ${manUnitedWins} games`)
リファクタ1
enum
を用いるのと、別のロジッククラスを用意してリファクタする。
src/index.ts
import { CsvFileReader } from './CsvFileReader' const reader = new CsvFileReader('football.csv') reader.read() enum MatchResult { HomeWin = 'H', AwayWin = 'A', Draw = 'D', } let manUnitedWins = 0 for (const match of reader.data) { if (match[1] === 'Man United' && match[5] === MatchResult.HomeWin) { manUnitedWins++ } else if (match[2] === 'Man United' && match[5] === MatchResult.AwayWin) { manUnitedWins++ } } console.log(`Man United won ${manUnitedWins} games`)
src/CsvFileReader.ts
import * as fs from 'fs' export class CsvFileReader { data: string[][] = [] constructor(public filename: string) {} read(): void { this.data = fs .readFileSync(this.filename, { encoding: 'utf-8', }) .split('\n') .map((row: string): string[] => row.split(',')) } }
いつenum
を使うべきか?
- Follow near-identical syntax rules as normal objects
- Creates an object with the same keys and values when converted from TS to JS
- Primary goal is to signal to to other engineers that these are all closely related values
- Use whenever we have a small fixed set of values that are all closely related and known at compile time
enum
を導入するかどうかの実例
- カラーピッカーで色指定をするとき:
yes
- 既に決まっていてわかっているから
- 動画配信サイトのカテゴリを指定するとき:
no
- 変わり続けてアップデートされるものだから
- メニュー上のドリンクのサイズ:
yes
- 選択肢が数種類しかないので
- 1750年以降の年数:
no
- 値の数が大きすぎる
- テキストメッセージの既読フラグ:
yes
- 想定しうるステータスが決まっているから