From ad42cfc76e8fb9cd706348720e14b511e43338fd Mon Sep 17 00:00:00 2001 From: codecalm Date: Sat, 8 Aug 2026 18:11:46 +0200 Subject: [PATCH] Replace remaining any casts in shared with proper types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ui/Table.astro: type the people rows with a local Person interface instead of Record[]. Surfaced a latent type mismatch: passed a string id into a prop typed as number (only silently worked because of the any cast) — replaced with , which resolves to the same person object without the redundant numeric re-lookup. - ui/Select.astro: add SelectOption/SelectData types for selects.json entries instead of two Record casts. - components/navbar/NavbarMenu.astro: type Level1.children as Record (matching NavbarMenuItem's own Level2/Level3 shapes) instead of Record, removing the `as any` casts at both NavbarMenuItem call sites. - components/parts/Tasks.astro: add a Task interface instead of Record[]; also drops the now-redundant inline subtask type annotation. No behavior change other than the Table.astro Avatar fix above. Verified: astro check 0/0/0 in shared, astro build succeeds for preview (127 pages) and docs (128 pages), vitest passes (38 tests). --- shared/components/navbar/NavbarMenu.astro | 18 +++++++++++++++--- shared/components/parts/Tasks.astro | 19 +++++++++++++++++-- shared/ui/Select.astro | 17 +++++++++++++++-- shared/ui/Table.astro | 13 +++++++++++-- 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/shared/components/navbar/NavbarMenu.astro b/shared/components/navbar/NavbarMenu.astro index 6bfe77b24..eee6b1aaf 100644 --- a/shared/components/navbar/NavbarMenu.astro +++ b/shared/components/navbar/NavbarMenu.astro @@ -5,6 +5,18 @@ import NavbarMenuItem from './NavbarMenuItem.astro' import menu from '@data/menu.json' import menuSample from '@data/menu-sample.json' +interface Level3 { + title: string + url?: string + badge?: string +} + +interface Level2 extends Level3 { + icon?: string + color?: string + children?: Record +} + interface Level1 { 'title': string 'title-long'?: string @@ -15,7 +27,7 @@ interface Level1 { 'disabled'?: boolean 'right'?: boolean 'columns'?: number - 'children'?: Record> + 'children'?: Record } interface Props { @@ -80,13 +92,13 @@ const chunk = (arr: T[], size: number): T[][] => { {columns.map((column) => ( ))} ) : ( - children.map(([childKey, child]) => ) + children.map(([childKey, child]) => ) )} )} diff --git a/shared/components/parts/Tasks.astro b/shared/components/parts/Tasks.astro index 5afb1f2d0..d604627a9 100644 --- a/shared/components/parts/Tasks.astro +++ b/shared/components/parts/Tasks.astro @@ -6,9 +6,24 @@ import AvatarList from '@ui/AvatarList.astro' import CardTitle from '@ui/CardTitle.astro' import tasksData from '@data/tasks.json' +interface Task { + 'name': string + 'color'?: string + 'starred'?: boolean + 'description'?: string + 'image'?: string + 'users'?: number + 'users-offset'?: number + 'due-date'?: string + 'favorite'?: boolean + 'likes'?: number + 'subtasks'?: { name: string; done?: boolean }[] + 'comments'?: number +} + interface Column { name: string - tasks: Record[] + tasks: Task[] } interface Props { @@ -100,7 +115,7 @@ const { data = tasksData as unknown as { columns: Column[] }, class: className } {task.subtasks && (
- {task.subtasks.map((subtask: { name: string; done?: boolean }) => ( + {task.subtasks.map((subtask) => (
{subtask.done ? ( diff --git a/shared/ui/Select.astro b/shared/ui/Select.astro index d727e90c5..6d33d0254 100644 --- a/shared/ui/Select.astro +++ b/shared/ui/Select.astro @@ -20,6 +20,19 @@ interface Props { showSearch?: boolean; } +interface SelectOption { + name: string; + flag?: string; + label?: string; + selected?: boolean; +} + +interface SelectData { + multiple?: boolean; + data?: string; + options?: string[] | { title: string; options: string[] }[] | Record; +} + const { id: idProp, key: keyProp, @@ -35,7 +48,7 @@ const { const id = idProp ?? keyProp; const key = keyProp ?? 'people'; -const data = (selects as Record)[key] ?? {}; +const data = (selects as Record)[key] ?? {}; const selectClass = [ 'form-select', @@ -78,7 +91,7 @@ if (values) { options = (data.options as string[]).map((option) => ({ value: option, text: option })); } else if (data.options) { // object of { key: { name, flag?, label?, selected? } } — with optional indicator - options = Object.entries(data.options as Record).map(([optKey, v]) => { + options = Object.entries(data.options as Record).map(([optKey, v]) => { let custom: string | undefined; if (indicator === 'flag') custom = ``; else if (indicator === 'label') custom = `${v.label}`; diff --git a/shared/ui/Table.astro b/shared/ui/Table.astro index 75ec87a5a..67f25b083 100644 --- a/shared/ui/Table.astro +++ b/shared/ui/Table.astro @@ -6,6 +6,15 @@ import DropdownMenu from './DropdownMenu.astro' import people from '@data/people.json' import { randomNumber } from '@shared/lib/pseudo-random' +interface Person { + id: string + full_name?: string + email?: string + job_title?: string + department?: string + [key: string]: unknown +} + interface Props { card?: boolean limit?: number @@ -22,7 +31,7 @@ const { card, limit = 5, stripped, offset, avatars, mobile, buttons, nowrap } = const roles = ['User', 'Admin', 'Owner'] const start = offset ?? 0 -const rows = (people as Record[]).slice(start, start + limit) +const rows = (people as Person[]).slice(start, start + limit) const tableClasses = ['table', 'table-vcenter', mobile && 'table-mobile-md', card && 'card-table', stripped && 'table-striped', nowrap && 'table-nowrap'] --- @@ -52,7 +61,7 @@ const tableClasses = ['table', 'table-vcenter', mobile && 'table-mobile-md', car {avatars ? (
- +
{person.full_name}