From afa2e4999b5bfd1ae23136cb5b90a7ed9288c3e2 Mon Sep 17 00:00:00 2001 From: Asep Haryana Saputra <90584806+MythEclipse@users.noreply.github.com> Date: Fri, 22 May 2026 16:19:10 +0000 Subject: [PATCH] fix: type diagnosis record mapping - Replace `function toReview(row: any)` with typed ReviewRow interface - Reconstruct DiseaseCatalogItem explicitly from selected fields instead of casting - Replace `.filter(Boolean)` with typed isDiagnosisRecord predicate - Add proper type narrowing for verdict and correctedDiseaseSlug in toReview Co-Authored-By: Claude Opus 4.7 --- apps/api/src/routes/diagnoses.ts | 58 +++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/apps/api/src/routes/diagnoses.ts b/apps/api/src/routes/diagnoses.ts index 4bbe740..4d8f1fd 100644 --- a/apps/api/src/routes/diagnoses.ts +++ b/apps/api/src/routes/diagnoses.ts @@ -10,26 +10,47 @@ import { classifyImage } from '../lib/image-model'; import { uploadImageToStorage } from '../lib/uploader-client'; import { env } from '../config/env'; -function toReview(row: any): ExpertReviewRecord | null { +interface ReviewRow { + reviewId: string | null; + reviewDiagnosisId: string | null; + reviewExpertId: string | null; + reviewVerdict: string | null; + reviewCorrectedDiseaseSlug: string | null; + reviewNotes: string | null; + reviewCreatedAt: Date | null; + expertId: string | null; + expertEmail: string | null; + expertName: string | null; +} + +function toReview(row: ReviewRow): ExpertReviewRecord | null { if (!row.reviewId) return null; + const verdict = row.reviewVerdict as 'verified' | 'corrected'; + const correctedDiseaseSlug = row.reviewCorrectedDiseaseSlug && isDiseaseSlug(row.reviewCorrectedDiseaseSlug) ? row.reviewCorrectedDiseaseSlug : null; + const notes = row.reviewNotes || ''; + return { id: row.reviewId, - diagnosisId: row.reviewDiagnosisId, - expertId: row.reviewExpertId, - verdict: row.reviewVerdict, - correctedDiseaseSlug: row.reviewCorrectedDiseaseSlug, - notes: row.reviewNotes, - createdAt: row.reviewCreatedAt.toISOString(), + diagnosisId: row.reviewDiagnosisId!, + expertId: row.reviewExpertId!, + verdict, + correctedDiseaseSlug, + notes, + createdAt: row.reviewCreatedAt!.toISOString(), expert: { - id: row.expertId, - email: row.expertEmail, - name: row.expertName, + id: row.expertId!, + email: row.expertEmail!, + name: row.expertName!, role: 'expert', }, }; } +function isDiagnosisRecord(record: DiagnosisRecord | null): record is DiagnosisRecord { + return record !== null; +} + async function loadDiagnosisRecord(id: string, userId: string | null, expertAccess: boolean): Promise { const db = createDbClient(); const filters = [eq(diagnoses.id, id)]; @@ -101,7 +122,20 @@ async function loadDiagnosisRecord(id: string, userId: string | null, expertAcce const predictedDiseaseSlug = row.predictedDiseaseSlug && isDiseaseSlug(row.predictedDiseaseSlug) ? row.predictedDiseaseSlug : null; const status = row.status as DiagnosisStatus; - const disease = row.disease && row.disease.slug ? (row.disease as DiseaseCatalogItem) : null; + const disease = row.disease && row.disease.slug && isDiseaseSlug(row.disease.slug) + ? ({ + slug: row.disease.slug, + label: row.disease.label, + commonName: row.disease.commonName, + summary: row.disease.summary, + description: row.disease.description, + symptoms: row.disease.symptoms, + recommendations: row.disease.recommendations, + riskLevel: row.disease.riskLevel, + accentColor: row.disease.accentColor, + displayOrder: row.disease.displayOrder, + } as DiseaseCatalogItem) + : null; return { id: row.id, @@ -223,7 +257,7 @@ export const diagnosisRoutes = new Elysia({ prefix: '/api/v1' }) .limit(30); const records = await Promise.all(rows.map((row) => loadDiagnosisRecord(row.id, user.id, false))); - return records.filter(Boolean); + return records.filter(isDiagnosisRecord); } catch (error) { return serviceUnavailable('Database unavailable'); }