From f9397f0faf8aed724830e95a1ec4ecf6ebb56a32 Mon Sep 17 00:00:00 2001 From: Edvin Gunic Date: Mon, 14 Sep 2026 00:04:41 +0200 Subject: [PATCH] feat: define TypeScript types for country data Replace temporary API types with shared response and Country models, retain only required fields, and map country codes for component use without introducing any. Closes #8 --- country-explorer/src/api/README.md | 41 +++++++++++++----- country-explorer/src/api/countries.ts | 45 +++++--------------- country-explorer/src/types/country.ts | 61 +++++++++++---------------- 3 files changed, 65 insertions(+), 82 deletions(-) diff --git a/country-explorer/src/api/README.md b/country-explorer/src/api/README.md index a084bc0..baec3d9 100644 --- a/country-explorer/src/api/README.md +++ b/country-explorer/src/api/README.md @@ -71,17 +71,36 @@ This list includes a handful of dependent territories alongside sovereign states ## Data fields used -| Field (from the wireframe) | API key(s) | Notes | -| -------------------------- | -------------------------- | --------------------------------------------------------------------------------------- | -| Name | `name` | | -| Flag | `flags.svg`, `flags.png` | `flag` also gives an emoji flag | -| Capital | `capital` | | -| Region | `region`, `subregion` | Used by `RegionFilter` | -| Population | `population` | | -| Area | `area` | In km² | -| Currency | `currencies` | | -| Languages | `languages` | | -| — | `alpha2Code`, `alpha3Code` | Not shown in the UI; used internally as a stable id/key and for `/alpha/{code}` lookups | +| Field (from the wireframe) | API key(s) | Notes | +| -------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| Name | `name` | | +| Flag | `Mapped to flagUrl, `flags.png` | `flag` also gives an emoji flag | +| Capital | `capital` | | +| Region | `region`, `subregion` | Used by `RegionFilter` | +| Population | `population` | | +| Area | `area` | In km² | +| Currency | `currencies` | | +| Languages | `languages` | | +| — | `alpha2Code`, `Mapped to code for selection, favorites, and lookups` | Not shown in the UI; used internally as a stable id/key and for `/alpha/{code}` lookups | + +## TypeScript models + +The shared types are defined in `src/types/country.ts`: + +- `CountryApiResponse` describes the API fields used for one country. +- `CountriesApiResponse` describes the array returned by the region endpoint. +- `Country` describes the internal data model used by components. + +The `toCountry` function in `src/api/countries.ts` converts API data +into the internal model: + +- `alpha3Code` becomes `code`, a stable country identifier. +- `flags.svg` becomes `flagUrl`. +- Currencies become strings containing the currency name and code. +- Languages become strings containing the language name. + +Only fields required by the application are retained. +Favorite status is managed separately as application state. ## Error handling diff --git a/country-explorer/src/api/countries.ts b/country-explorer/src/api/countries.ts index dce6375..4f9144f 100644 --- a/country-explorer/src/api/countries.ts +++ b/country-explorer/src/api/countries.ts @@ -1,3 +1,5 @@ +import type { CountriesApiResponse, Country, CountryApiResponse } from '../types/country'; + const BASE_URL = 'https://countries.dev'; // Thrown for a failed request: a non-2xx response (status holds the real @@ -12,40 +14,9 @@ export class ApiError extends Error { } } -// The internal shape this module returns to the rest of the app. This is a -// placeholder until #8 ("Define TypeScript Types for Country Data") lands -// its own Country type in src/types/country.ts — once it does, delete this -// and the RawCountry type below, import that one instead, and point -// `toCountry` at it. Kept local to this file (instead of touching -// src/types/country.ts) so the two issues can be worked on in parallel -// without both branches editing the same file. -export interface Country { - name: string; - capital: string; - region: string; - subregion: string; - population: number; - area: number; - flagUrl: string; - currencies: string[]; - languages: string[]; -} - -// The relevant slice of the raw shape returned by https://countries.dev. -interface RawCountry { - name: string; - capital: string; - region: string; - subregion: string; - population: number; - area: number; - flags: { svg: string; png: string }; - currencies: { code: string; name: string; symbol: string }[]; - languages: { name: string }[]; -} - -function toCountry(raw: RawCountry): Country { +function toCountry(raw: CountryApiResponse): Country { return { + code: raw.alpha3Code, name: raw.name, capital: raw.capital, region: raw.region, @@ -84,12 +55,16 @@ async function fetchJson(url: string): Promise { // GET /region/{region} — every country in a region, e.g. "europe". export async function getCountriesByRegion(region: string): Promise { - const raw = await fetchJson(`${BASE_URL}/region/${encodeURIComponent(region)}`); + const raw = await fetchJson( + `${BASE_URL}/region/${encodeURIComponent(region)}`, + ); + return raw.map(toCountry); } // GET /alpha/{code} — a single country by ISO alpha-2 or alpha-3 code. export async function getCountryByAlpha(code: string): Promise { - const raw = await fetchJson(`${BASE_URL}/alpha/${encodeURIComponent(code)}`); + const raw = await fetchJson(`${BASE_URL}/alpha/${encodeURIComponent(code)}`); + return toCountry(raw); } diff --git a/country-explorer/src/types/country.ts b/country-explorer/src/types/country.ts index 2bd537b..3e9a2b4 100644 --- a/country-explorer/src/types/country.ts +++ b/country-explorer/src/types/country.ts @@ -1,48 +1,37 @@ -export interface Currency { - code: string; - name: string; - symbol: string; -} - -export interface Language { +// Relevant fields from a successful single-country API response. +export interface CountryApiResponse { + alpha3Code: string; name: string; - nativeName: string; - iso639_1: string; - iso639_2: string; + capital: string; + region: string; + subregion: string; + population: number; + area: number; + flags: { + svg: string; + }; + currencies: { + code: string; + name: string; + }[]; + languages: { + name: string; + }[]; } -export interface Flags { - png: string; - svg: string; -} +// Successful response from /region/{region}. +export type CountriesApiResponse = CountryApiResponse[]; -// Shape returned by https://countries.dev for both `/region/{region}` (as an -// array) and `/alpha/{code}` (as a single object). A few fields are missing -// on some entries (e.g. dependent territories don't have `cioc`/`gini`, and -// some don't have `borders`), so they're marked optional here. +// Internal country model used by application components. export interface Country { + code: string; name: string; - nativeName: string; - alpha2Code: string; - alpha3Code: string; - numericCode: string; capital: string; region: string; subregion: string; population: number; - populationDensity: number; area: number; - currencies: Currency[]; - languages: Language[]; - timezones: string[]; - latlng: [number, number]; - demonym: string; - flag: string; - flags: Flags; - independent: boolean; - callingCodes: string[]; - topLevelDomain: string[]; - borders?: string[]; - cioc?: string; - gini?: number; + flagUrl: string; + currencies: string[]; + languages: string[]; }