Skip to content

Type Utilities

Type utilities provide stronger type safety guarantees when working with flag codes in TypeScript.

When working with flag codes from external sources (APIs, user input, databases), you often face a trade-off:

  • Loose typing (string): Runtime errors, no autocomplete
  • Strict typing (FlagCode): Compile-time errors, but requires validation

Type utilities bridge this gap by providing:

  1. Runtime validation with isFlagCode()
  2. Type coercion with coerceFlagCode()
  3. Type-safe unions with FlagCode type
import { coerceFlagCode, isFlagCode } from '@sankyu/react-circle-flags'
import { type FlagCode } from '@sankyu/react-circle-flags'

A union type of all valid country/subdivision codes.

type FlagCode = 'ad' | 'ae' | 'af' | ... | 'zw' | 'gb-eng' | 'gb-nir' | ...

Usage:

// ✅ Valid - TypeScript will autocomplete all 400+ codes
const code: FlagCode = 'us'
// ❌ Error - Type '"invalid"' is not assignable to type 'FlagCode'
const invalid: FlagCode = 'invalid'

isFlagCode(code: string): code is FlagCode

Section titled “isFlagCode(code: string): code is FlagCode”

Type guard that checks if a string is a valid flag code.

isFlagCode('us') // true
isFlagCode('US') // false (expects normalized lowercase input)
isFlagCode('invalid') // false

coerceFlagCode(code: string, fallback?: FlagCode): FlagCode

Section titled “coerceFlagCode(code: string, fallback?: FlagCode): FlagCode”

Safely converts a string to a valid flag code.

  • If the value is a valid flag code (case-insensitive), returns the lowercase version
  • Otherwise, returns the fallback (default: 'xx')
coerceFlagCode('US') // 'us'
coerceFlagCode('Us') // 'us'
coerceFlagCode('invalid') // 'xx' (default fallback)
coerceFlagCode('invalid', 'un') // 'un' (custom fallback)
coerceFlagCode('GB-ENG') // 'gb-eng'

Use isFlagCode() in conditionals to narrow types:

import { isFlagCode } from '@sankyu/react-circle-flags'
import { DynamicFlag } from '@sankyu/react-circle-flags'
export function CountryFlag({ code }: { code: string }) {
// Validate and normalize first
const normalized = code.trim().toLowerCase()
if (isFlagCode(normalized)) {
// TypeScript knows `normalized` is FlagCode here
return <DynamicFlag code={normalized} strict width={32} height={32} />
}
// Fallback for invalid codes
return <span className="text-red-500">{code.toUpperCase()} (invalid)</span>
}

Use coerceFlagCode() to handle unknown strings safely:

import { coerceFlagCode } from '@sankyu/react-circle-flags'
import { DynamicFlag } from '@sankyu/react-circle-flags'
export function CountryFlag({ codeFromApi }: { codeFromApi: string }) {
// Coerce to a safe FlagCode (invalid codes become 'xx')
const safeCode = coerceFlagCode(codeFromApi)
// Now we can use strict mode with full type safety
return <DynamicFlag code={safeCode} strict width={32} height={32} />
}

Provide a custom fallback for invalid codes:

import { coerceFlagCode } from '@sankyu/react-circle-flags'
import { DynamicFlag } from '@sankyu/react-circle-flags'
import { type FlagCode } from '@sankyu/react-circle-flags'
export function CountryFlag({
code,
fallbackCode = 'un',
}: {
code: string
fallbackCode?: FlagCode
}) {
// Use a custom fallback instead of 'xx'
const safeCode = coerceFlagCode(code, fallbackCode)
return <DynamicFlag code={safeCode} strict width={32} height={32} />
}

Validate form input with type safety:

import { useState } from 'react'
import { isFlagCode, type FlagCode } from '@sankyu/react-circle-flags'
export function CountryForm() {
const [code, setCode] = useState('')
const [error, setError] = useState('')
const handleSubmit = () => {
const normalized = code.trim().toLowerCase()
if (!isFlagCode(normalized)) {
setError('Invalid country code')
return
}
// TypeScript knows `normalized` is FlagCode here
const validCode: FlagCode = normalized
console.log('Valid code:', validCode)
setError('')
}
return (
<form onSubmit={e => e.preventDefault()}>
<input
value={code}
onChange={e => setCode(e.target.value)}
placeholder="Enter country code"
/>
{error && <p className="text-red-500">{error}</p>}
<button onClick={handleSubmit}>Submit</button>
</form>
)
}

Combine type utilities with strict prop for maximum type safety:

import { coerceFlagCode } from '@sankyu/react-circle-flags'
import { DynamicFlag } from '@sankyu/react-circle-flags'
export function CountryFlag({ codeFromApi }: { codeFromApi: string }) {
// Coerce unknown string to valid FlagCode
const safeCode = coerceFlagCode(codeFromApi)
// strict mode ensures TypeScript validates the code at compile time
return <DynamicFlag code={safeCode} strict width={32} height={32} />
}
UtilityRuntime CheckReturn TypeUse Case
isFlagCode()Yescode is FlagCodeType narrowing in conditionals
coerceFlagCode()YesFlagCodeSafe conversion with fallback
FlagCode typeNo (zero-cost)FlagCodeCompile-time type annotation