transform.types.ts
The transform.types.ts file contains TypeScript interface definitions for transformed data structures. These types define the clean, UI-ready shape of data after transformation from GraphQL responses.
File Location
Section titled “File Location”Page-Level Transform Types
Section titled “Page-Level Transform Types”src/pages/{routeName}/_graphql/transform.types.tsExamples:
src/pages/articles/_graphql/transform.types.tssrc/pages/places/_graphql/transform.types.tssrc/pages/articles/[slug]/_graphql/transform.types.tsComponent-Level Transform Types
Section titled “Component-Level Transform Types”src/components/{feature}/graphql/transform.types.tsExamples:
src/components/relatedArticles/graphql/transform.types.tssrc/components/hero/graphql/transform.types.tsFile Purpose
Section titled “File Purpose”The transform.types.ts file:
- Defines UI-ready interfaces - Clean types for component consumption
- Simplifies GraphQL types - Removes GraphQL-specific complexity
- Documents data shape - Clear contract between API and UI
- Enables type safety - TypeScript validation throughout the app
- Separates concerns - Decouples UI types from API types
Type Naming Pattern
Section titled “Type Naming Pattern”Transformed{Entity}{Entity}Data{Feature}PropsExamples:
export interface TransformedArticle { }export interface HeroData { }export interface ContentBlock { }export interface ArticleMetadata { }Typical Structure
Section titled “Typical Structure”// Main page/component data interfaceexport interface TransformedArticle { title: string; slug: string; publishDate: string; hero: HeroData; content: ContentBlock[]; metadata: ArticleMetadata; relatedArticles: ArticleListItem[];}
// Nested data structuresexport interface HeroData { title: string; subtitle: string | null; image: ImageData | null; cta: CTAData | null;}
export interface ImageData { url: string; alt: string; width: number; height: number;}
export interface CTAData { text: string; url: string; variant: "primary" | "secondary";}
// Content blocks with discriminated unionsexport type ContentBlock = | TextBlock | ImageBlock | VideoBlock | QuoteBlock;
export interface TextBlock { type: "text"; content: string;}
export interface ImageBlock { type: "image"; image: ImageData; caption: string | null;}
// Array item typesexport interface ArticleListItem { title: string; slug: string; excerpt: string; image: ImageData | null; publishDate: string;}
// Metadata and supporting typesexport interface ArticleMetadata { tags: string[]; category: string; readTime: number; author: AuthorData;}
export interface AuthorData { name: string; slug: string; image: ImageData | null;}Best Practices
Section titled “Best Practices”1. Use Descriptive Names
Section titled “1. Use Descriptive Names”Choose clear, specific names that describe the data:
// ✅ Good: Clear and specificexport interface TransformedArticle { }export interface HeroData { }export interface ContentBlock { }
// ❌ Bad: Vague or genericexport interface Data { }export interface Info { }export interface Response { }2. Document Complex Types
Section titled “2. Document Complex Types”Add JSDoc comments for complex or non-obvious types:
/** * Represents a content block in an article. * Uses discriminated union with `type` field for type safety. */export type ContentBlock = | TextBlock | ImageBlock | VideoBlock;
/** * Article metadata including SEO and social sharing information. */export interface ArticleMetadata { /** Page title for SEO */ title: string; /** Meta description for search engines */ description: string; /** Open Graph image for social sharing */ ogImage: ImageData | null;}3. Use Discriminated Unions
Section titled “3. Use Discriminated Unions”For content with multiple variants, use discriminated unions:
// ✅ Good: Type-safe union with discriminantexport type ContentBlock = | { type: "text"; content: string } | { type: "image"; image: ImageData; caption: string } | { type: "video"; videoUrl: string; thumbnail: ImageData };
// Usage provides type narrowingfunction renderBlock(block: ContentBlock) { switch (block.type) { case "text": return block.content; // TypeScript knows this is string case "image": return block.image.url; // TypeScript knows this exists case "video": return block.videoUrl; // TypeScript knows this exists }}
// ❌ Bad: Untagged union without discriminantexport type ContentBlock = | TextBlock | ImageBlock | VideoBlock;4. Prefer Interfaces Over Types
Section titled “4. Prefer Interfaces Over Types”Use interfaces for object shapes (better error messages, extendable):
// ✅ Good: Interface for object shapeexport interface TransformedArticle { title: string; content: ContentBlock[];}
// ✅ Good: Type alias for unionsexport type ContentBlock = TextBlock | ImageBlock;
// ❌ Less ideal: Type for object shapeexport type TransformedArticle = { title: string; content: ContentBlock[];};5. Make Nullability Explicit
Section titled “5. Make Nullability Explicit”Be explicit about which fields can be null:
// ✅ Good: Clear null handlingexport interface ArticleData { title: string; // Required subtitle: string | null; // Explicitly nullable image: ImageData | null; // Explicitly nullable tags: string[]; // Required (can be empty array)}
// ❌ Bad: Unclear nullabilityexport interface ArticleData { title: string; subtitle?: string; // Undefined vs null is ambiguous image: ImageData; // Looks required but might be null from API}6. Group Related Types
Section titled “6. Group Related Types”Organize types logically with comments:
// === Main Data Types ===
export interface TransformedArticle { }export interface TransformedPlace { }
// === Content Block Types ===
export type ContentBlock = TextBlock | ImageBlock | VideoBlock;export interface TextBlock { }export interface ImageBlock { }export interface VideoBlock { }
// === Metadata Types ===
export interface ArticleMetadata { }export interface PlaceMetadata { }
// === Shared Component Types ===
export interface ImageData { }export interface CTAData { }export interface AuthorData { }Common Patterns
Section titled “Common Patterns”Array Item Types
Section titled “Array Item Types”Define specific types for list items:
// Full article typeexport interface TransformedArticle { title: string; content: string; // ... many more fields}
// Minimal type for list viewsexport interface ArticleListItem { title: string; slug: string; excerpt: string; image: ImageData | null; publishDate: string;}
// Can extend for different contextsexport interface ArticleFeatured extends ArticleListItem { featured: true; priority: number;}Reusable Component Types
Section titled “Reusable Component Types”Define types for reusable UI components:
// Generic image type used across the appexport interface ImageData { url: string; alt: string; width: number; height: number;}
// Generic link/CTA typeexport interface CTAData { text: string; url: string; variant: "primary" | "secondary" | "text"; external?: boolean;}
// Generic tag typeexport interface TagData { slug: string; title: string; color?: string;}Nested Data Structures
Section titled “Nested Data Structures”Handle deeply nested data with intermediate types:
export interface TransformedArticle { title: string; hero: HeroData; sections: SectionData[];}
export interface HeroData { title: string; background: BackgroundData; overlay: OverlayData | null;}
export interface BackgroundData { type: "image" | "video"; image?: ImageData; video?: VideoData;}
export interface OverlayData { opacity: number; color: string;}Relationship to Generated Types
Section titled “Relationship to Generated Types”Transform types are distinct from generated GraphQL types:
// Generated type from GraphQL Codegen (complex, nullable fields)import type { ArticlePageQuery } from "#graphql/generated/contentful/schema";
// Transform type (clean, UI-ready)export interface TransformedArticle { title: string; content: ContentBlock[];}
// Transform function bridges the twoexport function transformArticleData( data: ArticlePageQuery): TransformedArticle { // Convert from generated type to transform type}Related
Section titled “Related”- transform.ts - Transform functions that use these types
- api.ts - API functions that return transform types
- GraphQL Query Patterns - Type patterns in context