Type Reuse Guide
Documentation for Developers
📋 Type Reuse Guide: No Local Types Policy
🚨 CRITICAL RULE: Never Define Local Types
In our NX monorepo, NEVER define types/interfaces locally in service or component files!
This policy prevents build issues, promotes code reuse, and maintains consistency across the monorepo.
✅ Correct Pattern: Models Directory
Required Structure
Every NX library MUST follow this structure:
libs/my-library/
├── src/
│ ├── lib/
│ │ ├── models/ # ← REQUIRED: All types go here
│ │ │ ├── index.ts # ← Export all models
│ │ │ ├── my-types.ts # ← Type definitions
│ │ │ └── interfaces.ts # ← More type definitions
│ │ ├── services/
│ │ ├── components/
│ │ └── index.ts
│ └── index.ts # ← Must export models
Step 1: Define Types in Models Directory
// libs/my-library/src/lib/models/my-service.types.ts
import type { SafeAny } from '@libs-foundational/core-models';
export interface MyServiceConfig {
apiUrl: string;
timeout?: number;
retries?: number;
}
export interface MyServiceState {
loading: boolean;
error: Error | null;
data: SafeAny[];
}
Step 2: Export from Models Index
// libs/my-library/src/lib/models/index.ts
export * from './my-service.types';
export * from './my-component.types';
export * from './my-interfaces';
Step 3: Export from Library Root
// libs/my-library/src/lib/index.ts
export * from './models'; // ← Export models FIRST
export * from './services';
export * from './components';
Step 4: Import in Services/Components
// libs/my-library/src/lib/services/my-service.ts
import { Injectable } from '@angular/core';
import { MyServiceConfig, MyServiceState } from '../models'; // ← Import from models
@Injectable({ providedIn: 'root' })
export class MyService {
private state: MyServiceState = {
loading: false,
error: null,
data: []
};
configure(config: MyServiceConfig): void {
// Implementation using imported types
}
}
❌ Wrong Patterns to Avoid
❌ Local Type Definitions
// ❌ NEVER DO THIS: Local interface in service file
import { Injectable } from '@angular/core';
interface MyServiceConfig { // ← DON'T DEFINE TYPES HERE!
apiUrl: string;
}
@Injectable()
export class MyService {
// ...
}
❌ Inline Type Definitions
// ❌ NEVER DO THIS: Inline types in components
@Component({...})
export class MyComponent {
@Input() config!: { // ← DON'T DEFINE TYPES INLINE!
title: string;
data: any;
};
}
❌ Exporting Types from Services
// ❌ NEVER DO THIS: Exporting types from service files
export interface MyConfig { ... } // ← DON'T EXPORT FROM SERVICES!
@Injectable()
export class MyService { ... }
🔧 Benefits of This Pattern
1. Prevents Build Issues
- Eliminates TypeScript compilation errors
- Avoids circular dependency issues
- Fixes "Cannot destructure property 'pos'" errors
2. Promotes Code Reuse
- Types can be imported by multiple services/components
- Consistent type definitions across the library
- Easy to refactor and maintain
3. Better Architecture
- Clear separation of concerns
- Types are discoverable in one location
- Easier to document and maintain
🎯 Real-World Examples
Example 1: UX Menu Library
// libs/ux-menu/src/lib/models/ux-menu.models.ts
export interface UxMenuItem {
id: string;
label: string;
icon?: string;
disabled?: boolean;
}
export interface UxMenuConfig {
id: string;
items: UxMenuItem[];
theme?: string;
}
// libs/ux-menu/src/lib/services/ux-menu.service.ts
import { Injectable } from '@angular/core';
import { UxMenuItem, UxMenuConfig } from '../models'; // ← Import from models
@Injectable()
export class UxMenuService {
private config: UxMenuConfig | null = null;
setConfig(config: UxMenuConfig): void {
this.config = config;
}
}
Example 2: Storage Library
// libs/storage-system/src/lib/models/storage.types.ts
export interface StorageConfig {
adapter: 'memory' | 'indexeddb' | 'localstorage';
namespace: string;
compression?: boolean;
}
export interface StorageItem<T = any> {
key: string;
value: T;
timestamp: number;
}
// libs/storage-system/src/lib/services/storage.service.ts
import { Injectable } from '@angular/core';
import { StorageConfig, StorageItem } from '../models'; // ← Import from models
@Injectable()
export class StorageService {
private config: StorageConfig | null = null;
async getItem<T>(key: string): Promise<StorageItem<T> | null> {
// Implementation using imported types
}
}
🚨 Common Build Error Solutions
Error: "Cannot destructure property 'pos'"
Cause: Local type definitions causing TypeScript compilation issues Solution: Move all types to models directory
// ❌ Before: Causes build errors
interface LocalType { ... } // ← Problematic
export class MyService { ... }
// ✅ After: Fixes build errors
import { MyType } from '../models'; // ← From models directory
export class MyService { ... }
Error: "Comments in CSS use '/* ... */' instead of '//'"
Cause: Using JavaScript-style comments in CSS/SCSS files Solution: Use proper CSS comment syntax
/* ❌ Before: Causes CSS syntax errors */
// Enhanced Navigation Controls
/* ✅ After: Proper CSS comments */
/* Enhanced Navigation Controls */
Error: "Expected ':' [css-syntax-error]"
Cause: Extra characters in CSS properties Solution: Remove invalid characters
/* ❌ Before: Causes CSS syntax errors */
padding-top: 1rem;z
/* ✅ After: Clean CSS properties */
padding-top: 1rem;
Error: "Duplicate key in object literal"
Cause: Duplicate keys in package.json scripts Solution: Rename duplicate keys
{
"//2.1": "=== DASHBOARD AUTO-RELOAD COMMANDS ===",
"//2.2": "=== DASHBOARD AUTO-RELOAD COMMANDS ==="
}
Error: "Circular dependency detected"
Cause: Services importing/exporting types incorrectly Solution: Centralize all types in models directory
// ❌ Before: Circular dependencies
// service-a.ts exports types
// service-b.ts imports from service-a.ts
// service-a.ts imports from service-b.ts
// ✅ After: No circular dependencies
// models/index.ts exports all types
// service-a.ts imports from models
// service-b.ts imports from models
🔄 Migration Steps
If You Have Local Types in Existing Code:
Create models directory:
mkdir -p libs/my-library/src/lib/modelsMove all type definitions:
// Cut from service files, paste into models/my-types.ts export interface MyType { ... }Create models index:
// libs/my-library/src/lib/models/index.ts export * from './my-types';Update imports:
// In service files, replace local types with: import { MyType } from '../models';Export from library root:
// libs/my-library/src/lib/index.ts export * from './models'; // ← Add this first
✅ Checklist for New Libraries
- Create
models/directory - Define all types in models directory (never in services/components)
- Create
models/index.tsthat exports all types - Export models from library root (
lib/index.ts) - Import types using
from '../models'pattern - Never define interfaces/types locally in service/component files
📚 Related Documentation
- Add New Library Guide - Covers the complete library setup
- Imports & Exports Guide - Path resolution strategies
- Setting Up NX Libraries - Build configuration
Remember: This pattern is CRITICAL for preventing build issues and maintaining code quality. Always follow the "No Local Types" policy!