diff --git a/docs/dev-notes/2025-10-22/fix-type-errors/plan.md b/docs/dev-notes/2025-10-22/fix-type-errors/plan.md new file mode 100644 index 000000000..262027359 --- /dev/null +++ b/docs/dev-notes/2025-10-22/fix-type-errors/plan.md @@ -0,0 +1,76 @@ +# TypeScript 5.9 型推論の理解と実装 + +## 概要 + +TypeScript 5.9 の型の厳格化により、`prisma/seed.ts` のメソッド引数に型注釈が必要になった。関数パラメータに対して正しい型を推論し、適用した。 + +## Q&A 要約 + +### Q1: `(typeof users)[number]` とは? + +**A:** 配列型 `users` から要素型を抽出する型操作。 + +- `typeof users` → 配列型そのもの +- `[number]` → 任意の数値インデックスでアクセスした時の型(全ての要素の共通型) + +### Q2: `[number]` は配列のインデックスか? + +**A:** 配列の「最初の要素」ではなく、「任意のインデックスでアクセスした結果の型」を意味する型操作。全インデックスで同じ型が返される。 + +### Q3: `ReturnType` とは? + +**A:** 関数 `hoge` の戻り値の型を自動抽出する型操作。複雑なファクトリー関数の戻り値型を手動定義せずに取得できる。 + +## 実装結果 + +**すべての関数パラメータの型注釈を追加し、関数引数の型推論エラーを完全に解消した。** + +```typescript +// addUser 関数 +async function addUser( + user: (typeof users)[number], + password: string, + userFactory: ReturnType, + keyFactory: ReturnType, +); + +// addTask 関数 +async function addTask( + task: (typeof tasks)[number], + taskFactory: ReturnType, +); + +// addWorkBook 関数 +async function addWorkBook( + workbook: (typeof workbooks)[number], + workBookFactory: ReturnType, +); + +// addTag 関数 +async function addTag(tag: (typeof tags)[number], tagFactory: ReturnType); + +// addTaskTag 関数 +async function addTaskTag( + task_tag: (typeof task_tags)[number], + taskTagFactory: ReturnType, +); + +// addSubmissionStatus 関数 +async function addSubmissionStatus( + submission_status: (typeof submission_statuses)[number], + submissionStatusFactory: ReturnType, +); + +// addAnswer 関数 +async function addAnswer( + answer: (typeof answers)[number], + taskAnswerFactory: ReturnType, +); +``` + +## 教訓 + +1. **型インデックスアクセス**: `Type[KeyType]` で型から値を抽出できる +2. **配列要素型の抽出**: `(typeof array)[number]` でシンプルに要素型を取得 +3. **関数戻り値型の自動抽出**: `ReturnType` で複雑な型定義を避けられる +4. **TypeScript 5.9 の型安全性**: 関数パラメータの暗黙的 `any` を禁止することで、デバッグ時の型ミスマッチを防げる diff --git a/prisma/seed.ts b/prisma/seed.ts index e3f7d1a54..fc54ea6ea 100755 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -20,6 +20,8 @@ import { import PQueue from 'p-queue'; import { generateLuciaPasswordHash } from 'lucia/utils'; +import { getTaskGrade } from '../src/lib/types/task'; + import { classifyContest } from '../src/lib/utils/contest'; import { users, USER_PASSWORD_FOR_SEED } from './users'; @@ -104,7 +106,12 @@ async function addUsers() { // See: // https://lucia-auth.com/reference/lucia/modules/utils/#generateluciapasswordhash -async function addUser(user, password: string, userFactory, keyFactory) { +async function addUser( + user: (typeof users)[number], + password: string, + userFactory: ReturnType, + keyFactory: ReturnType, +) { const currentUser = await userFactory.createForConnect({ id: user.id, username: user.name, @@ -150,7 +157,10 @@ async function addTasks() { console.log('Finished adding tasks.'); } -async function addTask(task, taskFactory) { +async function addTask( + task: (typeof tasks)[number], + taskFactory: ReturnType, +) { // Note: Task-Tag relationships are handled separately via TaskTag table await taskFactory.create({ contest_type: classifyContest(task.contest_id), @@ -158,7 +168,7 @@ async function addTask(task, taskFactory) { task_table_index: task.problem_index, task_id: task.id, title: task.title, - grade: task.grade, + grade: getTaskGrade(task.grade as string), }); } @@ -223,7 +233,10 @@ async function addWorkBooks() { console.log('Finished adding workbooks.'); } -async function addWorkBook(workbook, workBookFactory) { +async function addWorkBook( + workbook: (typeof workbooks)[number], + workBookFactory: ReturnType, +) { const urlSlug = normalizeUrlSlug(workbook.urlSlug); await workBookFactory.create({ @@ -293,7 +306,7 @@ async function addTags() { console.log('Finished adding tags.'); } -async function addTag(tag, tagFactory) { +async function addTag(tag: (typeof tags)[number], tagFactory: ReturnType) { // Note: Tags and Tasks are connected via the TaskTag relationship table // which is handled separately in addTaskTags() await tagFactory.create({ @@ -353,7 +366,10 @@ async function addTaskTags() { await taskTagQueue.onIdle(); // Wait for all task tags to complete console.log('Finished adding task tags.'); } -async function addTaskTag(task_tag, taskTagFactory) { +async function addTaskTag( + task_tag: (typeof task_tags)[number], + taskTagFactory: ReturnType, +) { await taskTagFactory.create({ id: task_tag.id, priority: task_tag.priority, @@ -398,7 +414,10 @@ async function addSubmissionStatuses() { console.log('Finished adding submission statuses.'); } -async function addSubmissionStatus(submission_status, submissionStatusFactory) { +async function addSubmissionStatus( + submission_status: (typeof submission_statuses)[number], + submissionStatusFactory: ReturnType, +) { await submissionStatusFactory.create({ id: submission_status.id, status_name: submission_status.status_name, @@ -460,7 +479,10 @@ async function addAnswers() { console.log('Finished adding answers.'); } -async function addAnswer(answer, taskAnswerFactory) { +async function addAnswer( + answer: (typeof answers)[number], + taskAnswerFactory: ReturnType, +) { await taskAnswerFactory.create({ id: answer.id, task: {