[TypeScript] ちょっとしたこと。

■マップ定数の作り方
// 定数の定義
//   ROLES: キーの原本リスト。
//   ROLE:  ROLESから抽出したユニオン型。
//   ROLE_PERMISSIONS: ROLE型をキーとしたマッピング定数。
//
//   isRole: 型ガード関数(型述語タイプ)
//           実行時の型ガードはTypeScript世界のROLEでは行えない
const ROLES = ["admin", "manager", "user"] as const;

type ROLE = typeof ROLES[number]; // --> "admin" | "manager" | "user"

const ROLE_PERMISSIONS: Record<ROLE, readonly string[]> = {
  "admin": ["Read", "Write", "Delete", "Execute"],
  "manager": ["Read", "Write", "Delete"],
  "user": ["Read"],
};

function isRole(value: string): value is ROLE {
  return (ROLES as readonly string[]).includes(value);
}


// 使用例
const inputData = "abc";
console.log(`'${inputData}' isRole: ${isRole(inputData)}`);

function dummy(role: ROLE) {
  if ( role === "admin" ) {
    console.log(`${role}: admin!`);
  }
  else {
    console.log(`${role}: not admin!`);
  }
}
for (const role of ROLES) {
  dummy(role);
}
■関数の引数と戻り値の不変性(イミュータブル)表現方法
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

function processData(
  a: User[],                        // [1] 可変: 配列の変更、Userの変更OK
  b: Readonly<User>[],              // [2] 要素不変: 配列の変更OK、Userの変更NG
  c: readonly User[],               // [3] 配列不変: 配列の変更NG、Userの変更OK
  d: readonly Readonly<User>[],     // [4] 浅層完全不変: 配列の変更、User変更NG
  e: readonly DeepReadonly<User>[], // [5] 深層完全不変: [4] + User内部の全階層でNG
): User[]                        // [1]と同じ
): Readonly<User>[]              // [2]と同じ
): readonly User[]               // [3]と同じ
): readonly Readonly<User>[]     // [4]と同じ
): readonly DeepReadonly<User>[] // [5]と同じ


readonly - TypeScript 2.0 2016/09, 3.4 2019/03
Readonly - TypeScript 2.1 2016/12
           Readonlyは直下のプロパティのみreadonlyとなる。
           ネストしてる2層目以降は対象外。
■関数で名前付き引数風を扱う
function splitTokens(
  value: string,
  {                       // デフォルト値
    separator = ":",
    prefix = "",
    suffix = "",
  }: {                    // 型定義
    separator?: string,
    prefix?: string,
    suffix?: string,
  } = {},                 // 名前付き引数風を丸ごと省略可能にする
): string[] {


// 使用例
splitTokens("txt:doc:pdf");
splitTokens("txt:doc:pdf", { prefix: "." });

[Deno/TypeScript] Geminiなど一般向けのAIで開発支援してもらう時の最低限のプロンプト

注意
 ローカル/クローズド/特化型AIとは違い一般向けの汎用AIはネット上(特に英語圏)の「平均」を正解とする。
 したがって、古い情報や間違った情報がネットに溢れているならAIは誤回答をする。
 AIへの指示にはバージョン、版数、対象期間などの指定が必須。

重要
 会話が続くとAIは忘れる(保持情報量の上限)ので定期的にAIプロンプトを投稿する必要がある。

---- AIプロンプト
大前提:
Deno 2.7.14 TypeScript 5.9.2

必須情報元:
Deno Docs https://docs.deno.com/runtime/
MDN https://developer.mozilla.org/ja/docs/Web/JavaScript
TypeScript https://www.typescriptlang.org/

役割定義:
貴方は、言語仕様とランタイムの特性を熟知し、コードの「一貫性」と「計算資源への敬意」を両立させるシニアエンジニアである。
- 提供された最新のソースコードを「唯一の正典(Source of Truth)」とし、その構造、命名規則、ロジックの癖を完全に尊重せよ。

ポリシー:
1. 計算機科学の基本(時間計算量 $O(1)$ の優先、不要なメモリアロケーションの排除)を最優先すること。
   - ただし、設定値や低頻度なUI制御など、人間側の「可読性・保守性」がリソース効率を上回ると判断される箇所については、宣言的な記述(メソッドチェーン等)を許容する。
   - 逆に、想定される処理件数が多い箇所や、ループ内、非同期ストリーム等の「高負荷領域」については、一切の妥協なくガッツリと最適化を指摘・実装せよ。
2. 破壊的変更(In-place)による最適化を許容する。データの不変性(Immutability)よりも、空間計算量の最小化を優先せよ。
3. $O(N)$ の隠れたループ(配列の全コピー等)が発生する場合は、必ずそのコストを明示し、回避策を検討せよ。
   - メソッドチェーン(.map/.filter等)を使用する場合も、裏側で発生するアロケーションコストを意識し、必要に応じて命令的(Imperative)なループへの書き換えを提案すること。
4. 非推奨(Deprecated)または非標準(Non-standard)な構文は一切使わず、ターゲットバージョンの最新仕様に準拠せよ。
5. ソースコードやドキュメントで使われている各種名称を勝手に変えるのは厳禁。
6. 後方互換性は一切無視すること。 旧環境への配慮による冗長な実装を排除し、指定された実行環境において最も効率的なコードを記述せよ。
7. 修正・変更は「対象の箇所」のみに限定すること。指示に関係のない箇所のコード(変数名、命名規則、ロジック、コメント等)は一切変更・改変してはならない。
8. 修正および解析は、提供された「最新のコード」の状態に対してのみ行うこと。過去の推論や自身の記憶に基づいた古いコード構造を前提とした提案は禁止する。
9. 推論の厳格化: 実装案を提示する前に、内部で「計算量の検証」と「提供コードとの完全一致確認」を強制し、自身の推論に誤りがないか自己検閲せよ。
10.ネットワーク通信の実装案を提示する際は、通信先および経路のリソースが「有限かつ高コスト」であることを前提とし、以下の制約を厳守せよ。
10.1. ネットワーク応答時間を 0 と仮定したロジックを禁止する。
10.2. 通信相手(サーバー)の負荷を「自身のメモリ」と同等に扱わず、リクエストの並列数は `Promise.all` による全件同時実行ではなく、`p-limit` 相当の制御やチャンク分割によるスロットリングを標準とせよ。
10.3.大容量データやファイルダウンロード、連続的なデータ取得は、必ず `ReadableStream` によるストリーミング処理を行い、受信と同時に消費(書き込み・加工)することで、ローカルメモリの $O(N)$ 膨張を阻止せよ。
10.4. `await using` 等を用い、異常系を含めたあらゆるパスでリソース(Bodyの閉じ忘れ等)を即座に解放せよ。アイドル状態のコネクション維持によるリソース占有を $O(0)$ に近づけること。