| name | type-system-governance |
| description | Enforce TypeScript type organization rules. Use when creating types, interfaces, or DTOs. All types MUST go in /types directory - never inline in components. |
Type System Governance
⚠️ ALL types and interfaces MUST be defined in the /types directory.
This skill enforces strict type organization to prevent duplication, maintain DRY principle, and ensure consistent type imports across the codebase.
🚨 THE GOLDEN RULE
type ButtonProps = { variant: string };
export interface ButtonProps {
variant: ButtonVariant;
}
import type { ButtonProps } from "types/props";
ESLint enforces this rule - you will get lint errors for inline types.
📁 Type Organization Structure
types/
├── README.md # Type organization guide (READ THIS FIRST)
├── common.ts # Shared UI types
│ ├── NavigationItem
│ ├── SocialLinks
│ └── ...
├── props.ts # Component props types
│ ├── ButtonProps
│ ├── CardProps
│ └── ...
├── event.ts # Event domain types
│ ├── EventFilters
│ ├── EventCardData
│ └── distanceToRadius()
├── filters.ts # Filter system types
│ ├── FilterConfig
│ ├── FilterState
│ └── ...
├── i18n.ts # i18n types
│ ├── Locale
│ ├── SupportedLocale
│ └── ...
├── api/ # API DTOs
│ ├── event.ts # EventDTO, EventResponseDTO
│ ├── news.ts # NewsDTO, NewsResponseDTO
│ ├── city.ts # CitySummaryResponseDTO
│ ├── region.ts # RegionDTO
│ └── category.ts # CategoryDTO
└── index.ts # Re-exports (optional)
✅ Step-by-Step: Adding a New Type
1. Determine the Canonical Location
| Type Category | Location | Example |
|---|
| API response DTO | types/api/<domain>.ts | EventResponseDTO |
| Component props | types/props.ts | ButtonProps |
| Shared UI types | types/common.ts | NavigationItem |
| Domain-specific | types/<domain>.ts | FilterConfig |
| Utility types | Near usage in types/ | DateRange |
2. Check for Existing Types
grep -r "interface\|type " types/ --include="*.ts"
grep -r "ButtonProps\|CardProps" types/
cat types/common.ts | grep "export"
cat types/props.ts | grep "export"
3. Add Type to Correct File
export interface NewComponentProps {
variant: "primary" | "secondary";
size?: "sm" | "md" | "lg";
children: React.ReactNode;
}
4. Import and Use
import type { NewComponentProps } from "types/props";
export function NewComponent({
variant,
size = "md",
children,
}: NewComponentProps) {
}
🚫 Common Violations (And How to Fix)
❌ Violation 1: Inline Type Definition
type CardProps = {
title: string;
description: string;
};
export function Card({ title, description }: CardProps) {
}
Fix:
export interface CardProps {
title: string;
description: string;
}
import type { CardProps } from "types/props";
export function Card({ title, description }: CardProps) {
}
❌ Violation 2: Duplicate Type Definition
interface Event {
id: number;
title: string;
}
interface Event {
id: number;
title: string;
}
Fix:
export interface Event {
id: number;
title: string;
}
import type { Event } from "types/event";
❌ Violation 3: API DTO Not in /types/api
interface EventResponse {
content: Event[];
totalPages: number;
}
Fix:
export interface EventResponseDTO {
content: EventDTO[];
totalPages: number;
totalElements: number;
last: boolean;
}
import type { EventResponseDTO } from "types/api/event";
❌ Violation 4: Using any
const handleData = (data: any) => {
};
Fix:
const handleData = (data: unknown) => {
if (isEventData(data)) {
}
};
import type { EventData } from "types/event";
const handleData = (data: EventData) => {
};
📋 Type Consolidation Workflow
When you find duplicate types:
Step 1: Identify Duplicates
grep -r "interface\|type " --include="*.ts" --include="*.tsx" | grep -v "types/"
grep -rn "interface Event\|type Event" --include="*.ts"
Step 2: Choose Canonical Location
- Is it a DTO? →
types/api/<domain>.ts
- Is it UI props? →
types/props.ts
- Is it shared? →
types/common.ts
- Is it domain-specific? →
types/<domain>.ts
Step 3: Consolidate
export interface ConsolidatedType {
}
Step 4: Update All Imports
grep -rn "ConsolidatedType" --include="*.ts" --include="*.tsx"
import type { ConsolidatedType } from 'types/common';
Step 5: Verify
yarn typecheck && yarn lint
🔧 Quick Reference: Canonical Sources
| Type | Canonical File | Import Path |
|---|
NavigationItem | types/common.ts | types/common |
SocialLinks | types/common.ts | types/common |
ButtonProps | types/props.ts | types/props |
EventDTO | types/api/event.ts | types/api/event |
FilterConfig | types/filters.ts | types/filters |
CitySummaryResponseDTO | types/api/city.ts | types/api/city |
Locale | types/i18n.ts | types/i18n |
✅ Verification Checklist
Before submitting code with types:
🔍 ESLint Rule Enforcement
This project has ESLint rules that block type definitions outside /types:
{
rules: {
'no-restricted-syntax': [
'error',
{
selector: 'TSTypeAliasDeclaration',
message: 'Type aliases must be defined in /types directory'
},
{
selector: 'TSInterfaceDeclaration',
message: 'Interfaces must be defined in /types directory'
}
]
}
}
If you see this error, move your type to the appropriate file in /types.
💡 Pro Tips
Use Path Aliases
import type { Event } from "types/event";
import type { Event } from "../../../types/event";
Export Types Properly
export interface Event {
}
export type EventStatus = "draft" | "published";
export type { Event, EventStatus } from "./event";
Type vs Interface
export interface Event {
id: number;
title: string;
}
export type EventStatus = "draft" | "published" | "archived";
export type EventId = Event["id"];
Remember: Types in /types directory = maintainable codebase.
Last Updated: January 15, 2026