TypeScript Patterns
This guide documents the TypeScript patterns used in Lakea. These conventions ensure consistency and help newcomers write code that fits the existing style.
Module Resolution
Section titled “Module Resolution”We use "moduleResolution": "nodenext" which requires .js extensions in imports, even for .ts files.
// Correctexport * from './types/index.js';import { Star } from './types/index.js';
// Wrong - will fail at runtimeexport * from './types';import { Star } from './types/index';This is configured in tsconfig.base.json and applies to all packages.
Barrel Exports
Section titled “Barrel Exports”Every directory has an index.ts that re-exports its contents. This enables clean imports.
export * from './types/index.js';export * from './data-fetchers/index.js';export * from './apps/index.js';export * from './routing/index.js';Consumers can then import from the package root:
import { Star, fetchExoplanets, getAppLink } from '@lakea/core';Type Exports
Section titled “Type Exports”Use export type for type-only exports. This helps bundlers with tree-shaking.
// Exporting both value and typeexport { fetchExoplanets } from './exoplanetArchive.js';export type { FetchExoplanetsOptions } from './exoplanetArchive.js';Interface Naming
Section titled “Interface Naming”- Component props:
XxxProps(e.g.,ButtonProps,CardProps) - No
Iprefix (useButtonProps, notIButtonProps) - Descriptive names (use
DataSource, notISource)
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { variant?: ButtonVariant; size?: ButtonSize; children: ReactNode;}Variant Types
Section titled “Variant Types”Use string literal unions for component variants. Export the type separately for consumers.
export type ButtonVariant = 'primary' | 'secondary' | 'ghost';export type ButtonSize = 'sm' | 'md' | 'lg';Then create a style map:
const variantStyles: Record<ButtonVariant, string> = { primary: 'bg-cosmic-500 text-white hover:bg-cosmic-600', secondary: 'bg-slate-700 text-slate-100', ghost: 'bg-transparent text-slate-300',};Const Assertion
Section titled “Const Assertion”Use as const for immutable objects like design tokens:
export const colors = { cosmic: { 500: '#5a6cf4', 600: '#4a5cd4', }, slate: { 800: '#1e293b', 900: '#0f172a', },} as const;This ensures TypeScript infers literal types ('#5a6cf4') instead of string.
Generic Types
Section titled “Generic Types”Use descriptive single-letter generics (T for type, K for key):
export interface QueryResult<T> { data: T[]; totalCount: number; source: DataSource; pagination?: { offset: number; limit: number; hasMore: boolean; };}Null Handling
Section titled “Null Handling”Use nullish coalescing for default values and optional chaining for callbacks:
// Nullish coalescing - preserves 0 and '' but replaces null/undefinedconst mass = row.pl_masse ?? undefined;
// Optional chaining for callbacksonPointClick?.(point);
// Combined patternconst value = data?.results?.[0]?.value ?? 'default';Strict Mode
Section titled “Strict Mode”All packages use strict TypeScript settings from tsconfig.base.json:
{ "compilerOptions": { "strict": true, "noUnusedLocals": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "noImplicitOverride": true }}Never disable these settings. If you get a type error, fix the code, don’t weaken the types.
Path Aliases
Section titled “Path Aliases”Development imports use the @lakea/source custom condition for source file access:
{ "customConditions": ["@lakea/source"], "paths": { "@lakea/core": ["packages/core/src/index.ts"], "@lakea/design-system": ["packages/design-system/src/index.ts"] }}This allows importing source files directly during development while using dist files in production.
Package Exports
Section titled “Package Exports”Each package has dual exports for dev and prod:
{ "exports": { ".": { "@lakea/source": "./src/index.ts", "types": "./dist/index.d.ts", "import": "./dist/index.js" } }}File Naming
Section titled “File Naming”- Config files: lowercase (
vite.config.ts,tsconfig.json) - Source files: camelCase (
exoplanetArchive.ts,fetchNasaData.ts) - React components: PascalCase (
ScatterPlot.tsx,Button.tsx) - Directories: kebab-case (
data-fetchers/,design-system/)
Named vs Default Exports
Section titled “Named vs Default Exports”Library packages use named exports only:
// Correct - named exportexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(...);
// Wrong in library packages - default exportexport default Button;App components may use default exports for page components.