tsconfig.json メモ

tsconfig.jsonとは

TypeScriptで書かれたコードをTSCコンパイルする際に、「どのファイルをコンパイルすべきか」、「コンパイルしたコードをどこに格納するのか」、「どのバージョンのJavaScriptコンパイルするのか」の設定を記述するファイル。

コンパイルとは、コンパイラーというプログラムによって、開発者が書いたコードを機械語に変換すること

コンパイル実行後:書いたコード→抽象構文木(AST)(空白、タブ、改行を無視したデータ構造)→バイトコード(よりハードウェアに近いコード)→バイトコードをランタイムに入力とした与える

TSの場合、TypeScriptのコードをTSCがASTを生成後、型チェックを行う。その後、JSのコードに変換され、JSがASTになり、上と同じ流れ

オプション(compilerOptions) 機能説明
include どのフォルダーのTypeScriptファイルをコンパイルするのか
exclude include で指定したファイルから特別に除外するファイルを記述する。
lib コードを実行する環境にどんなAPIが存在するのか
module TSCコンパイルする際に、どのモジュールシステムにコンパイルするのか(CommonJS, SystemJS, ES2015)
outDir 生成するJSコードをどのディレクトリに格納するのか(distとか?)
strict 型チェックを厳格にするか否か
target TSCコンパイルする際に、どのJavaScriptのバージョンにコンパイルするのか(ES3, ES5, ES2015, ES2016)
extends tsconfig.jsonを環境によって使いわけたりする際に使う
compileOnSave この値をtrueにすると、tsconfig.jsonを保存した際にコンパイルが走るようになる。
sourceMap jsのmapファイル。
baseUrl non-relativeなimportにおいて、相対的なカレントディレクトリをどこにするか指定する。値が./の場合はtsconfig.jsonが置いてあるディレクトリを指す。relativeなimportには一切関係しない。
noUnusedLocals 宣言されたが使用されていない変数が存在する場合にコンパイルエラー
noUnusedParameters 関数を定義しているのに使用されない場合にコンパイルエラー

https://qiita.com/ryokkkke/items/390647a7c26933940470

エラーハンドリング メモ

// 定義
const showName = (value) => {
  if (typeof value === "number") {
    // throwのプログラムが走ると自動的に次はcatch内の処理が走る
    // throw命令の後にはインスタンスを置くことができ(new Hoge())、そのインスタンスの中身をcatchは受け取ることができる
    // throwだけならそこで処理は止まるが、catchがある場合、続きの処理としてcatch内のプログラムが走るため、処理は止まらない
    throw new Error("数字だよ")

   // こっちで書く方がイメージしやすいかも
    const error = new Error("数字だよ");
    throw error;
  }
  console, log(value);
};


// 実行
try {
  showName(4);
} catch (e) {
  console.log("111", e);
  console.log("222", e.message);
}
111 Error: 数字だよ
    at showName (/Users/ogawayuuya/prapra/JavaScript/JS2/iii.js:5:11)
    at Object.<anonymous> (/Users/ogawayuuya/prapra/JavaScript/JS2/iii.js:13:3)
    at Module._compile (node:internal/modules/cjs/loader:1101:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:79:12)
    at node:internal/main/run_main_module:17:47
222 数字だよ

別にこれでもいい

const showName = (value) => {
  if (typeof value === "number") {
    // throwのプログラムが走ると自動的に次はcatch内の処理が走る
    // throw命令の後にはインスタンスを置くことができ(new Hoge())、そのインスタンスの中身をcatchは受け取ることができる
    throw new String("ヤッホー")
  }
  console, log(value);
};


try {
  showName(4);
} catch (e) {
  console.log("111", e);
}
111 [String: 'ヤッホー']

エラーの入れ子について

try {

  // 一個下の階層(普通は関数で梱包されている)
  try {
    const error = new Error("何らかのエラー発生");
    throw error;
  } catch (error) {
    // エラー内容を出力
    console.error('入れ子のcatchブロック:', error);

    // ここで処理を止めて、上の階層(catch)にエラーオブジェクトを渡す
    // 上の階層の「〜いろいろな処理〜」は全部すっとばされて、上階層のcatchのプログラムが走る
    throw error;
  }

  // 〜いろいろな処理〜

} catch (error) {  // 入れ子try文に`catch`がないので、ここで例外を受け取る
  console.error("catchブロック", error);
}
入れ子のcatchブロック: Error: 何らかのエラー発生
    at Object.<anonymous> (/Users/ogawayuuya/prapra/JavaScript/JS2/mmm.js:5:19)
    at Module._compile (node:internal/modules/cjs/loader:1101:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:79:12)
    at node:internal/main/run_main_module:17:47
catchブロック Error: 何らかのエラー発生
    at Object.<anonymous> (/Users/ogawayuuya/prapra/JavaScript/JS2/mmm.js:5:19)
    at Module._compile (node:internal/modules/cjs/loader:1101:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:79:12)
    at node:internal/main/run_main_module:17:47

エラーのカスタム

class HogeError extends Error {
  constructor(hoge = "ogawa", date, ...params) {
    super(...params)

    this.message = "hogeに関するエラーだよ"
    this.date = new Date()
    this.hoge = hoge;
  }
}

try {
  const e = new HogeError()
  throw e;
} catch (e) {
  console.log("111", e);
  console.log("222", e.message);
  console.log("333", e.hoge)
}
111 HogeError: hogeに関するエラーだよ
    at Object.<anonymous> (/Users/ogawayuuya/prapra/JavaScript/JS2/jjj.js:16:13)
    at Module._compile (node:internal/modules/cjs/loader:1101:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:79:12)
    at node:internal/main/run_main_module:17:47 {
  date: 2022-09-09T02:34:02.551Z,
  hoge: 'ogawa'
}
222 hogeに関するエラーだよ
333 ogawa

非同期処理のエラーハンドリング(Promise使う場合)

失敗例

try {
  // `setTimeout`を使用した非同期処理
  setTimeout(() => {
    throw new Error('非同期的なエラー');
  }, 1000);
} catch (error) {
  console.error('catchブロック:', error);
}
// 本当はcatchで止めたいのに、実行するとこの処理が最初に走ってしまう
console.log('後続にくる何らかのプログラム');
後続にくる何らかのプログラム
/Users/ogawayuuya/prapra/JavaScript/JS2/nnn.js:4
    throw new Error('非同期的なエラー');
    ^

Error: 非同期的なエラー
    at Timeout._onTimeout (/Users/ogawayuuya/prapra/JavaScript/JS2/nnn.js:4:11)
    at listOnTimeout (node:internal/timers:557:17)
    at processTimers (node:internal/timers:500:7)

成功例

const timer = () => {
  return new Promise((_, reject) => {
    setTimeout(() => {
      reject(new Error('非同期的なエラー'))
    }, 1000)
  })
}

timer()
  .catch((error) => console.error(error))
  .finally(() => console.log('後続にくる何らかのプログラム'))
Error: 非同期的なエラー
    at Timeout._onTimeout (/Users/ogawayuuya/prapra/JavaScript/JS2/ooo.js:4:14)
    at listOnTimeout (node:internal/timers:557:17)
    at processTimers (node:internal/timers:500:7)
後続にくる何らかのプログラム

非同期処理のエラーハンドリング(async, await使う場合)

const fetchApi = async () => {
  try {
    const res = await fetch('https://sample.com/api/items');
    if (!res.ok) {
      throw new Error('例外が発生!');
    }

    return res.data;
  } catch (error) {
    throw error;
  }
};

fetchApi()
.then(data => console.log(data))
.catch(error => console.error(error));

eslint, prettier メモ

概念・言葉の定義・メリットデメリットなど

ESLint

JavaScript のための静的検証ツール

  • コードを実行する前に明らかなバグを見つける
  • 括弧やスペースの使い方などのスタイルを統一したりするのに役立つ。

Prettier(プリティア

コードフォーマッター(ソースコードを整形してくれるツール)のこと。

  • ソースコードの品質を保つ(コードのスタイルの一貫性を保つため)
  • コードレビュー時に、設計や命名などの重要な箇所に集中するため(コードのスタイルの指摘に時間を割くのを防ぐため)
  • 複数のメンバーが各自の整形ルールを適用し、更新する度に余計な差分が発生することを防ぐため
  • ソースコードを綺麗にするための労力(スタイル定義の議論や時間)を費やさなくて済むため ツールに任せられることはツールに任せてしまった方が今後楽になるため

Prettier をESLint と併用する理由

Prettier の方がコード整形が優れているから

  • ESLint では整形できないコードを整形できる
  • ESLint と比べて手軽で確実に整形できる(eslint --fixは設定に該当したエラーのみを整形するから、設定に依存してしまう。厳密に設定しようとすると設定ファイルの中身が膨大になる。これに対してprettierはデフォルトの成形ツールが存在するため、eslintのようなデメリットがない)

もっとも、Prettier はコードフォーマッタのため、ESLint のような構文チェック機能はない。

そこで、コードの整形は Prettier が行い、コードの構文チェックは ESLint が行うように併用する。

しかし、ESLint にもフォーマットのルールが存在するため、ESLint の整形と Prettier の整形が競合する可能性がある。

そのため、ESLint のフォーマットのルールを無効化にして、Prettier と ESLint を実行できるようにする。

使い方

ESlint

①eslintrc.jsonファイル作成
$ eslint ファイル名 を実行

.eslintrc.jsonファイル

{
    "extends": ["eslint:recommended"],
    "plugins": [],
    "parserOptions": {},
    "env": {"browser": true},
    "globals": {},
    "rules": {}
}

$ eslint ファイル名を実行 (ディレクトリで指定することも可能)

⇨コードで問題のある箇所が検出される

.eslintrc.jsonファイルの設定について

  • root:ESLint は実行時のカレントディレクトリを起点にして、上位のディレクトリの設定ファイル (.eslintrc.*)を探す⇨ root: true の指定があると、カレントディレクトリを探しに行ってくれる。
  • env:実行環境の指示 例えば、Web ブラウザ上で動作させる JavaScript コードであれば browser、Node.js 環境で動作させるコードであれば node を true に設定。 内部的には、選択した環境ごとに globals オプションの設定が行われます。
  • parser ESLint は標準で JavaScript コードのパースに対応しているが、TypeScript コード (.ts、.tsx) を扱えるようにするには、TypeScript 用のパーサー (@typescript-eslint/parser) をインストールして指定する必要あり。
例.
parser: '@typescript-eslint/parser'
  • parserOptions:パーサーの設定 ESLint のデフォルトパーサーは ECMAScript 5 の構文で記述されたコードを想定。 別のバージョンの ECMAScript 構文で記述したいときは、ecmaVersion でバージョンを設定する必要あり。
  • plugins:使用するプラグインの指定
ESLint の組込みルールは汎用的なもの。特定のライブラリやフレームワーク、または実行環境に特化した検証は行わない。そのような検証はプラグインとして提供される。

プラグインとは ESLint の追加ルールをまとめた npm パッケージ。
利用するには、npm install コマンドでインストールして、設定ファイルの "plugins" プロパティを指定する。


このプラグインは次のようなことをチェックしてくれます。

require() したファイルが存在するかどうか
require() したモジュールがきちんと package.json の dependencies に存在するかどうか
非推奨になった API を使っていないかどうか
指定したバージョンの Node.js でサポートされていない ECMAScript 構文を使っていないかどうか
Shebang (#!/usr/bin/env node) の使い方が誤っていないかどうか
"error" ... ルール違反を見つけるとエラーにする (赤くなる)。
"warn" ..... ルール違反を見つけると警告にする (黄色くなる)。
"off" ....... ルール違反を見つけても何もしない。

設定例
"rules": {
   "semi": "error"
 }

オプションも使える
オプションを利用するときは、各ルールの値が配列になる。

Prettier

.prettierrcファイルを作成

  • printWidth:折り返す行の長さを指定。(デフォルトは80)
  • tabWidth:インデントのスペースの数を指定(デフォルトは2)
  • useTabs:スペースではなくタブで行をインデントして、trueを設定することでタブを適用する(デフォルトはfalse)
  • semiステートメントの最後にセミコロンを追加する。trueを設定することで最後にセミコロンを追加する(デフォルトはtrue。falseにするとセミコロンが無いとエラーになる箇所にだけセミコロンを追加する。)
  • singleQuote:ダブルクォートの代わりにシングルクォートを使用する。JSX quotesはこの項目を無視する(trueを設定することでシングルクォートを使う。デフォルトはfalse)
  • quoteProps:オブジェクトのプロパティが引用されるときに変更する。

    • as-needed : 必要な場合にのみ、オブジェクトプロパティを引用符で囲む(デフォルト)
    • consistent: オブジェクトの少なくとも1つのプロパティに引用符が必要な場合は、すべてのプロパティを引用符で囲む
    • preserve : 入力された引用符を尊重する
  • jsxSingleQuote:JSXでダブルクォートの代わりにシングルクォートを使用する(デフォルトはfalse)

  • trailingComma:末尾のカンマの設定。
    • es5 : ES5で有効な末尾のカンマ(オブジェクト、配列など)
    • none : 末尾にカンマをつけない(デフォルト)
    • all : 可能な限り末尾にカンマを付ける(関数の引数含む)
  • bracketSpacing:オブジェクトリテラルの角かっこの内側にスペースを入れる(デフォルトはtrue)
  • bracketSameLine:複数行の要素の最終行の最後に「>」を置く。falseは次の行に置く(デフォルトはfalse)
  • arrowParens:アロー関数の()が省略可能でも常に書く。
    • always : ()を常に書く
    • avoid : 省略可能なときは()を書かない(デフォルト)
  • proseWrapmarkdownの折返しの設定
    • always : Print Widthで指定した値を超える時は折り返す
    • never : 折り返さないしない
    • preserve : そのまま折り返す(デフォルト)
  • htmlWhitespaceSensitivity:HTMLファイルのグローバルな空白の感度を指定
    • css : displayプロパティのデフォルト値を尊重(デフォルト)
    • strict : 空白を区別する
    • ignore : 空白は区別しない
  • endOfLine:改行の文字コードを指定
    • lf : LinuxMacOS、gitリポジトリで一般的な、ラインフィード(\n)のみ
    • crlf : Windowsで一般的な、キャリッジリターン + ラインフィード文字(\r\n)
    • cr : キャリッジリターン文字(\r)のみ
    • auto : 既存の行末を維持(デフォルト)
  • embeddedLanguageFormatting:Prettierがファイルに埋め込まれた引用コードをフォーマットするかどうかを制御
    • off : 埋め込まれたコードを自動的にフォーマットしない
    • auto : Prettierが自動的に識別できる場合、埋め込みコードをフォーマットする(デフォルト)

TypeScriptのエラーハンドリングの出し方

テストでは、seedファイルからのテストDBへの流し込みとテスト自体の実行は別のプロセス(世界)で実行されてるらしい(別のメモリを確保してるってこと?).

なので、テストを実行した時のエラーはターミナル上に出力されるが、seed実行時のエラーは、ターミナルに出力されない

つまり、エラーをどこかに出力する必要がある(今回はfsというnodeにデフォルトで搭載されているエラー出力モジュールを使う)

import fs from 'fs'

try {
 // seedからDBに流し込む処理
} 
catch (e) {
  fs.writeFileSync('./error.txt', JSON.stringify(e))
}

以下が出力された

{"code":"P2002","clientVersion":"3.12.0","meta":{"target":["number"]}}

ここで、p2002 prismaでエラーコードを検索

検索結果が以下

"Unique constraint failed on the {constraint}"

DBのスキーマにユニーク制約があるカラムがあるにもかかわらず、そのカラムに同じ値を入れていることが原因であることがわかった

TypeScript メモ

オブジェクトのキーへ変換

obj = { a: "aaa", b: "bbb" }

// []で、文字列でもオブジェクトのキーにできる
obj__ = { [obj.a]: "aaaaa" }

console.log(obj__);
// => { aaa: 'aaaaa' }

https://github.com/softdevice-arsaga/hedgehog-eventAPI/pull/174/files#diff-094f07ff4ed62cf43cd0d65550a5f76383464b8bdf95ca8de34627943c67f93aR183-R198

as

アサーションです。 型アサーション(Type Assertion)とは、その推論された型や、既に型定義済みの変数の型を上書きします。

ケース1
const people = {};

people.name = "ogawa"
//=> プロパティ 'name' は型 '{}' に存在しません


ケース2
const people = {} as {
  name: String
}

people.name = "ogawa"
//=> エラーが出なくなる

スプレッド構文の「...」

配列の場合

const arr = [1, 2, 3]

const arr2 = [...arr]
console.log(arr2);
// [ 1, 2, 3 ]

const arr3 = [...arr, 4, 5]
console.log(arr3);
// [ 1, 2, 3, 4, 5 ]

※本当ならこう
arr.push(4, 5)
console.log(arr);
// [ 1, 2, 3, 4, 5 ]

オブジェクトの場合

const obj = { name: "太郎", age: 26 }

const obj2 = { ...obj }
console.log(obj2)
// { name: '太郎', age: 26 }

const obj3 = { ...obj, weight: 50 }
console.log(obj3)
// { name: '太郎', age: 26, weight: 50 }
console.log(obj)
// { name: '太郎', age: 26 }

※ 本当ならこんな感じ

obj.profession = "エンジニア"
console.log(obj)
// { name: '太郎', age: 26, profession: 'エンジニア' }


const obj_copy = Object.assign({}, obj)
console.log(obj_copy)
// { name: '太郎', age: 26 }

obj_copy.profession = "エンジニア"
console.log(obj_copy)
// { name: '太郎', age: 26, profession: 'エンジニア' }
console.log(obj)
// { name: '太郎', age: 26 }


const obj_add = Object.assign(obj, { age: 30, weight: 50 })
console.log(obj_add)
// { name: '太郎', age: 30, weight: 50 }
console.log(obj)
// { name: '太郎', age: 30, weight: 50 }

コンテナ: 互いに影響しない隔離された実行環境を提供する技術(それぞれの実行環境を独立させることで、ディレクトリの競合が起こらないメリット)

コンテナの中にはプログラムの実行に必要なライブラリやフレームワーク、基本コマンドを全て入れる(コンテナ内のプログラムが実行される時にコンテナ外の何かが参照されることはない)

Docker: コンテナを実現できるソフトウェアの一つ

Dockerイメージ: コンテナの中にはプログラムの実行に必要なライブラリやフレームワーク、基本コマンドを全て入れなければいけないが、linuxの基本コマンドやライブラリから作っていかなければいけないから大変。。 そこで、Dockerイメージ Dockerイメージが、コンテナ作りを楽にしてくれる。コンテナの元を提供してくれて、これをインストールすれば、コンテナ内のプログラムの実行に必要なライブラリやフレームワーク、基本コマンドが全て入ったコンテナを提供してくれる。

⇨Dockerイメージは、DockerHubに提供されているため、それを使ってもいいが、汎用的なものにすぎない。(汎用的なものを使う場合、docker run -eとして環境変数を設定したり、docker cpでファイルをコピーしたり、docker execでコンテナに入って操作したりしなければいけない) そこで、イメージを自作する。イメージを自作すると上記のようなDockerコンテナを作成した後のいろんな操作をイメージ段階で済ますことができるため、コンテナを作成するたびに上記の操作をする必要がなくなる。 Dockerイメージを作成するやり方は2パターン ①コンテナからイメージを作る方法(docker commitコマンドを使う) ②Dockerfileからイメージを作る方法(主流)

Dockerのメリット

  • コンテナは独立しており互いに干渉しないため、一台のサーバーに複数のシステムを構成できる

  • Dockerイメージを使うことで簡単に プログラムの実行環境を作れる

  • Dockerイメージを使うことで、全く同じ環境をコピーして作成することが可能

docker run -dit --name my-app -p 8080:80 -v "$PWD":/usr/local/apache2/htdocs/ httpd:2.4

マウントについて Dockerホストのファイルをコンテナ内のファイルにコピー(docker cp)したり、docker execでコンテナ内にファイルを作成したりできるが、これらはコンテナが破棄されると消えてしまう そこで、コンテナを破棄した際に失いたくないデータは コンテナの外(Dockerホスト)に出して、マウントする(コンテナ内にアクセスした時にDockerホスト内のファイルを参照するように設定する)。これがバインドマウント。

一方で、Dockerエンジン上に確保した領域をマウントする方法もある。これがボリュームマウント。(データベースのデータなどを保存しておく)。Dockerホストでの変更をコンテナ内のファイルに即座に反映させたい時なんかはバインドマウントを使う。

コンテナ同士の通信(ネットワーク)について Dockerホスト、各Dockerコンテナは一つの仮想的なbridgeネットワークで接続されているため、Dockerホストや各コンテナにはそれぞれIPアドレスが割り当てられる。

ただこのネットワークでコンテナ同士が通信しようとする場合、コンテナのIPを指定しなければならず、コンテナのIPはコンテナを作成し直せば変わってしまうため、IPでいちいち指定するのはとても不便。 そこで、Dockerネットワークを新規で作るのが良い。Dockerネットワークを新規で作成すれば、コンテナ名の指定で通信ができるようになる。

カリー化(JavaScript)

こういうやつ

// 定義
const hoge = (x) => 
  (y) => {}

// 実行
hoge(x)(y)
  • 高階関数」とは「関数それ自体」を「引数もしくは戻り値、あるいは両方」として扱う関数
  • カリー化はこの高階関数の性質を利用したもの

カリー前

第一引数が同じなのに何回も同じことを書かなきゃいけない(第一引数を固定した関数を共通化したい)

// カリー前
const getAddress = (prefecture, name, age) => {
  console.log(`${prefecture}住みの${name}さん${age}歳です`);
}

getAddress("愛知県", "佐藤", 20);
getAddress("愛知県", "鈴木", 21);
getAddress("愛知県", "伊藤", 19);
getAddress("東京都", "田中", 19);

カリー後

// カリー後
const getAddressCurry = (prefecture) => {
  // 関数を返す
  return (name, age) => {
    console.log(`${prefecture}住みの${name}さん${age}歳です`);
  }
}

// const getAddressCurry = (prefecture) =>
//   (name, age) => {
//     console.log(`${prefecture}住みの${name}さん${age}歳です`);
//   }


getAddressCurry("愛知県")("佐藤", 20);
getAddressCurry("愛知県")("鈴木", 21);
getAddressCurry("愛知県")("伊藤", 19);

getAddressCurry("東京都")("田中", 19);


// getAddressCurry("愛知県") は、「愛知県」が代入された以下の関数を戻り値として返す
// (name, age) => {
//   console.log(`愛知県住みの${name}さん${age}歳です`);
// }

const getAddressCurry_aichi = getAddressCurry("愛知県");
const getAddressCurry_tokyo = getAddressCurry("東京都");

getAddressCurry_aichi("佐藤", 20);
getAddressCurry_aichi("鈴木", 21);
getAddressCurry_aichi("伊藤", 19);

getAddressCurry_tokyo("田中", 19);

参考 https://zenn.dev/nekoniki/articles/5b7980fac91048775931

https://kazchimo.com/2021/03/29/monkey_curry/