Skip to content

Type Spotify API responses - #239

Merged
watsonbox merged 2 commits into
masterfrom
spotify-types
Oct 20, 2026
Merged

watsonbox merged 2 commits into
masterfrom
spotify-types

Conversation

@watsonbox

Copy link
Copy Markdown
Owner

Adds hand-written TypeScript types for the Spotify responses the app reads (src/spotify.ts), types the export config (ExportConfig) and the Liked Songs pseudo-playlist (LikedSongsPlaylist), removes every @ts-ignore and @ts-expect-error, and re-enables @typescript-eslint/no-explicit-any. One any remains, in the fixture scrubber, which handles raw captures from any endpoint.

The types are modelled on the recorded fixtures rather than the docs, including the nullable cases the app handles: null tracks, local tracks, null added_by, null preview_url and ISRC, null playlist images, null owner.display_name, null public, and null entries in /albums and /audio-features. The test builders annotate each fixture template with its type, so the compiler checks the types against the real responses.

Typing also showed that CSV cells hold numbers and booleans as well as strings. They're now typed as CsvValue, with no change to the output.

Hand-written types vs @types/spotify-api

I tried basing the types on @types/spotify-api@0.0.26:

  • Only PagingObject fitted as it is. The other 13 of the 14 object types needed overrides, 49 fields in all, and every batch and error response wrapper still had to be written locally. Assigning the fixtures to the raw package types failed for all 9 that were checked.
  • The package is typed from the docs, so it silently loses cases the app meets:
    • /me is typed as the private profile, which requires email, country, product and birthdate. We never request those scopes.
    • Episodes in playlists come back shaped like tracks. Their artist name is null and the show's name is in artist type, but the package has name: string and type: "artist".
    • Local tracks have null ids, uris, release dates and album types, and an empty external_urls. The package types all of these as present.
    • Image dimensions and playlist images can be null.
    • owner.display_name and added_by can be null.
    • /albums and /audio-features return null entries.
    • Literal type fields don't type-check against fixtures imported from JSON.
  • It saved only about 60 lines, in exchange for override layers that make hover output and type errors harder to read.

The package did show that the playlist public is nullable in Spotify's docs. That's ported to the hand-written types in b7203b5.

Behaviour change: episode artist names

Typing exposed a bug. For podcast episodes in a playlist, Spotify sends the artist (the show) with name: null, and the CSV wrote the literal text null into the Artist Name(s) and Album Artist Name(s) columns. Those cells are now blank. Only episode rows change.

🤖 Generated with Claude Code + hand edited

watsonbox and others added 2 commits October 20, 2026 19:05
Add hand-written types in src/spotify.ts for the responses the app reads, checked against the recorded fixtures through the typed test builders. Type the export config, the Liked Songs pseudo-playlist and the unloaded playlist placeholder, and re-enable @typescript-eslint/no-explicit-any.

Use a btn-xs class and inline Font Awesome icon tuples instead of the @ts-expect-error workarounds. Blank the artist names of podcast episodes, which Spotify sends as null and the CSV wrote out as "null".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spotify documents playlist public as nullable, which @types/spotify-api models and the hand-written types didn't.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@watsonbox watsonbox self-assigned this Oct 20, 2026
@watsonbox
watsonbox merged commit 089473d into master Oct 20, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant