Why
likes-row.tsx discriminates between TmdbItem and iTunesItem at runtime using 'artist' in item, a structural duck-type check that is invisible to the type system — a future field change could silently break the discrimination without a compile error.
Current state
ui/sections/likes-row.tsx line 34 uses const isItunesItem = 'artist' in item. Both TmdbItem and iTunesItem share fields id, title, date, imageUrl, link with no type discriminant field, so the runtime check is the only way to distinguish them.
Ideal state
TmdbItem carries type: 'tmdb' and iTunesItem carries type: 'itunes' as discriminant fields.
- The prop is typed as a proper tagged union
TmdbItem | iTunesItem.
- The
isItunesItem check is replaced by a switch (item.type) or equivalent that TypeScript narrows correctly.
- A future field change does not silently break the discrimination.
Starting points
ui/sections/likes-row.tsx — line 34 (the duck-type check to replace)
- Type definitions for
TmdbItem and iTunesItem — where the discriminant fields should be added
QA plan
- Open
ui/sections/likes-row.tsx and verify no 'artist' in item check exists.
- Remove the
type discriminant field from iTunesItem — expect a compile error in the component.
- Run
tsc --noEmit — expect no type errors with both discriminant fields present.
Done when
TmdbItem | iTunesItem is a discriminated union with a type field and the discrimination in likes-row.tsx is type-narrowing rather than a structural check.
Why
likes-row.tsxdiscriminates betweenTmdbItemandiTunesItemat runtime using'artist' in item, a structural duck-type check that is invisible to the type system — a future field change could silently break the discrimination without a compile error.Current state
ui/sections/likes-row.tsxline 34 usesconst isItunesItem = 'artist' in item. BothTmdbItemandiTunesItemshare fieldsid,title,date,imageUrl,linkwith notypediscriminant field, so the runtime check is the only way to distinguish them.Ideal state
TmdbItemcarriestype: 'tmdb'andiTunesItemcarriestype: 'itunes'as discriminant fields.TmdbItem | iTunesItem.isItunesItemcheck is replaced by aswitch (item.type)or equivalent that TypeScript narrows correctly.Starting points
ui/sections/likes-row.tsx— line 34 (the duck-type check to replace)TmdbItemandiTunesItem— where the discriminant fields should be addedQA plan
ui/sections/likes-row.tsxand verify no'artist' in itemcheck exists.typediscriminant field fromiTunesItem— expect a compile error in the component.tsc --noEmit— expect no type errors with both discriminant fields present.Done when
TmdbItem | iTunesItemis a discriminated union with atypefield and the discrimination inlikes-row.tsxis type-narrowing rather than a structural check.