Prerequisites
- Understanding of TypeScript type system and interfaces ๐
- Knowledge of ES6 modules and import/export statements โก
- Familiarity with TypeScript compilation process ๐ป
What you'll learn
- Master type-only import syntax and best practices ๐ฏ
- Optimize bundle size by eliminating unnecessary runtime imports ๐๏ธ
- Implement clean separation between types and values ๐
- Create efficient type-only module architectures โจ
๐ฏ Introduction
Welcome to the elegant world of type-only imports! ๐ญ If regular imports were like bringing both the script and the actors to a theater performance, then type-only imports are like bringing just the script - you get all the information you need for the show without the extra overhead of the physical actors taking up space!
Type-only imports are a powerful TypeScript feature that allows you to import types, interfaces, and type aliases without including any runtime code in your bundle. This creates cleaner architectures, smaller bundle sizes, and better separation of concerns between compile-time type checking and runtime behavior.
By the end of this tutorial, youโll be a type-only import expert, capable of creating optimized TypeScript architectures that maintain excellent type safety while minimizing runtime overhead. Letโs explore the art of zero-cost type imports! ๐
๐ Understanding Type-Only Imports
๐ค What Are Type-Only Imports?
Type-only imports allow you to import type information without importing any runtime values. Theyโre completely erased during compilation, leaving no trace in the final JavaScript output while providing full type checking during development.
// ๐ Regular imports - include both types AND runtime values
import { UserService, User, UserRole } from './UserModule';
// This imports:
// - UserService (class/function - runtime value) โ
// - User (interface - type only) โ
// - UserRole (enum/type - could be runtime or type) โ
// โก Type-only imports - ONLY types, zero runtime overhead
import type { User, UserRole, UserPermissions } from './UserModule';
import { UserService } from './UserModule'; // Only the runtime value
// ๐ฏ Mixed imports - both types and values in one statement
import { UserService, type User, type UserRole } from './UserModule';
// โจ This clearly separates what's needed at runtime vs compile-time
// ๐ฆ Before type-only imports (bundle includes everything)
import {
UserInterface, // Type only, but bundled anyway
UserPermissions, // Type only, but bundled anyway
UserService, // Runtime value, needed in bundle
validateUser, // Runtime function, needed in bundle
UserRole // Could be enum (runtime) or type alias
} from './UserModule';
// ๐ฆ After type-only imports (optimized bundle)
import type {
UserInterface, // Type only, completely erased
UserPermissions, // Type only, completely erased
UserRole // Type only, completely erased
} from './UserModule';
import {
UserService, // Runtime value, included in bundle
validateUser // Runtime function, included in bundle
} from './UserModule';
// ๐ฎ Let's explore comprehensive type-only import patterns
console.log('๐ญ Type-Only Import Examples');
// ๐ Example module structure for demonstration:
// src/
// โโโ types/
// โ โโโ User.ts // Pure type definitions
// โ โโโ Api.ts // API type definitions
// โ โโโ Common.ts // Shared type utilities
// โโโ services/
// โ โโโ UserService.ts // Runtime service + types
// โ โโโ ApiService.ts // Runtime service + types
// โ โโโ ValidationService.ts
// โโโ utils/
// โโโ typeGuards.ts // Runtime type checking
// โโโ validators.ts // Runtime validation
// ๐ฏ Pure type-only module (types/User.ts)
export interface User {
id: string;
name: string;
email: string;
role: UserRole;
permissions: UserPermissions[];
metadata: UserMetadata;
}
export interface UserRole {
id: string;
name: string;
level: number;
}
export interface UserPermissions {
resource: string;
actions: string[];
conditions?: Record<string, any>;
}
export interface UserMetadata {
createdAt: Date;
updatedAt: Date;
lastLogin?: Date;
preferences: UserPreferences;
}
export interface UserPreferences {
theme: 'light' | 'dark' | 'auto';
language: string;
notifications: NotificationSettings;
}
export interface NotificationSettings {
email: boolean;
push: boolean;
sms: boolean;
frequency: 'immediate' | 'daily' | 'weekly';
}
// ๐ฏ Type utilities for user operations
export type CreateUserRequest = Omit<User, 'id' | 'metadata'>;
export type UpdateUserRequest = Partial<Pick<User, 'name' | 'email' | 'role'>>;
export type UserResponse = User & { token?: string };
export type UserListResponse = {
users: User[];
total: number;
page: number;
limit: number;
};
// ๐ง Advanced type utilities
export type UserWithoutSensitiveData = Omit<User, 'permissions' | 'metadata'>;
export type UserRoleNames = UserRole['name'];
export type PermissionActions = UserPermissions['actions'][number];
// ๐๏ธ Type-only import analyzer class
class TypeOnlyImportAnalyzer {
// ๐ Track import usage statistics
private importStats = {
typeOnly: 0,
valueOnly: 0,
mixed: 0,
total: 0
};
// ๐ Module dependency graph
private dependencyGraph = new Map<string, {
typeImports: Set<string>;
valueImports: Set<string>;
fromModules: Set<string>;
}>();
// ๐ Analyze import statement
analyzeImport(
importStatement: string,
fromModule: string,
targetModule: string
): {
type: 'type-only' | 'value-only' | 'mixed';
typeImports: string[];
valueImports: string[];
optimizationSuggestions: string[];
} {
console.log(`๐ Analyzing import: ${importStatement}`);
const typeImports: string[] = [];
const valueImports: string[] = [];
const optimizationSuggestions: string[] = [];
// ๐ฏ Parse import statement (simplified parsing)
const isTypeOnlyImport = importStatement.includes('import type');
const hasTypeKeyword = importStatement.includes('type ');
// ๐ Extract imported names (simplified)
const importMatch = importStatement.match(/import\s+(?:type\s+)?{([^}]+)}/);
if (importMatch) {
const imports = importMatch[1].split(',').map(imp => imp.trim());
imports.forEach(imp => {
if (imp.startsWith('type ')) {
typeImports.push(imp.replace('type ', ''));
} else if (isTypeOnlyImport) {
typeImports.push(imp);
} else {
// ๐ค Could be either - needs analysis
if (this.isKnownType(imp, targetModule)) {
typeImports.push(imp);
optimizationSuggestions.push(
`Consider using 'type ${imp}' for type-only import`
);
} else {
valueImports.push(imp);
}
}
});
}
// ๐ Update statistics
let importType: 'type-only' | 'value-only' | 'mixed';
if (typeImports.length > 0 && valueImports.length === 0) {
importType = 'type-only';
this.importStats.typeOnly++;
} else if (valueImports.length > 0 && typeImports.length === 0) {
importType = 'value-only';
this.importStats.valueOnly++;
} else {
importType = 'mixed';
this.importStats.mixed++;
}
this.importStats.total++;
// ๐๏ธ Update dependency graph
this.updateDependencyGraph(fromModule, targetModule, typeImports, valueImports);
// ๐ก Generate optimization suggestions
if (importType === 'mixed' && typeImports.length > 0) {
optimizationSuggestions.push(
'Consider separating type and value imports for better optimization'
);
}
if (typeImports.length > valueImports.length && !isTypeOnlyImport) {
optimizationSuggestions.push(
'Most imports are types - consider using type-only import'
);
}
console.log(`๐ Import analysis complete: ${importType}`);
return {
type: importType,
typeImports,
valueImports,
optimizationSuggestions
};
}
// ๐ Check if import is a known type
private isKnownType(importName: string, module: string): boolean {
// ๐ฏ Type detection heuristics
const typePatterns = [
/^[A-Z]\w*$/, // PascalCase (likely interface/type)
/Interface$/, // Ends with Interface
/Type$/, // Ends with Type
/Props$/, // React props
/Config$/, // Configuration types
/Options$/, // Option types
/Response$/, // API response types
/Request$/ // API request types
];
// ๐ Known type modules
const typeModules = [
'./types/',
'./interfaces/',
'./models/',
'@types/'
];
// ๐ Check patterns
if (typePatterns.some(pattern => pattern.test(importName))) {
return true;
}
// ๐ Check if from type-only module
if (typeModules.some(typeModule => module.includes(typeModule))) {
return true;
}
return false;
}
// ๐๏ธ Update dependency graph
private updateDependencyGraph(
fromModule: string,
targetModule: string,
typeImports: string[],
valueImports: string[]
): void {
if (!this.dependencyGraph.has(fromModule)) {
this.dependencyGraph.set(fromModule, {
typeImports: new Set(),
valueImports: new Set(),
fromModules: new Set()
});
}
const moduleData = this.dependencyGraph.get(fromModule)!;
typeImports.forEach(imp => moduleData.typeImports.add(imp));
valueImports.forEach(imp => moduleData.valueImports.add(imp));
moduleData.fromModules.add(targetModule);
}
// ๐ Generate optimization report
generateOptimizationReport(): {
summary: string;
statistics: typeof this.importStats;
recommendations: string[];
potentialSavings: number;
} {
const recommendations: string[] = [];
let potentialSavings = 0;
// ๐ Analyze statistics
const mixedImportRatio = this.importStats.mixed / this.importStats.total;
const typeImportRatio = this.importStats.typeOnly / this.importStats.total;
if (mixedImportRatio > 0.3) {
recommendations.push(
'High number of mixed imports detected - consider separating type and value imports'
);
potentialSavings += this.importStats.mixed * 0.1; // Estimated 10% savings per mixed import
}
if (typeImportRatio < 0.5) {
recommendations.push(
'Low usage of type-only imports - review imports for optimization opportunities'
);
}
// ๐ Analyze dependency graph
for (const [module, data] of this.dependencyGraph) {
const typeCount = data.typeImports.size;
const valueCount = data.valueImports.size;
if (typeCount > valueCount * 2) {
recommendations.push(
`Module ${module} imports mostly types - consider type-only imports`
);
potentialSavings += typeCount * 0.05; // Estimated savings
}
}
const summary = `Analyzed ${this.importStats.total} imports: ${this.importStats.typeOnly} type-only, ${this.importStats.valueOnly} value-only, ${this.importStats.mixed} mixed`;
return {
summary,
statistics: { ...this.importStats },
recommendations,
potentialSavings: Math.round(potentialSavings * 100) / 100
};
}
// ๐๏ธ Generate optimized import suggestions
generateOptimizedImports(
fromModule: string
): {
original: string[];
optimized: string[];
explanation: string[];
} {
const moduleData = this.dependencyGraph.get(fromModule);
if (!moduleData) {
return { original: [], optimized: [], explanation: [] };
}
const original: string[] = [];
const optimized: string[] = [];
const explanation: string[] = [];
// ๐ฏ Group by target module
const moduleGroups = new Map<string, {
types: string[];
values: string[];
}>();
// ๐ Organize imports by target module
for (const targetModule of moduleData.fromModules) {
if (!moduleGroups.has(targetModule)) {
moduleGroups.set(targetModule, { types: [], values: [] });
}
}
// ๐ง Generate optimized import statements
for (const [targetModule, imports] of moduleGroups) {
const { types, values } = imports;
if (types.length > 0 && values.length > 0) {
// Mixed import - separate them
original.push(`import { ${[...types, ...values].join(', ')} } from '${targetModule}';`);
optimized.push(`import type { ${types.join(', ')} } from '${targetModule}';`);
optimized.push(`import { ${values.join(', ')} } from '${targetModule}';`);
explanation.push(`Separated types and values for ${targetModule} to optimize bundle size`);
} else if (types.length > 0) {
// Type-only import
original.push(`import { ${types.join(', ')} } from '${targetModule}';`);
optimized.push(`import type { ${types.join(', ')} } from '${targetModule}';`);
explanation.push(`Converted to type-only import for ${targetModule}`);
} else if (values.length > 0) {
// Value-only import - already optimized
const statement = `import { ${values.join(', ')} } from '${targetModule}';`;
original.push(statement);
optimized.push(statement);
explanation.push(`Value-only import for ${targetModule} is already optimized`);
}
}
return { original, optimized, explanation };
}
// ๐งน Reset analyzer
reset(): void {
this.importStats = {
typeOnly: 0,
valueOnly: 0,
mixed: 0,
total: 0
};
this.dependencyGraph.clear();
console.log('๐งน Import analyzer reset');
}
}
// ๐ฎ Usage examples
const typeImportDemo = (): void => {
const analyzer = new TypeOnlyImportAnalyzer();
// ๐ Analyze different import patterns
const imports = [
"import { User, UserService, validateUser } from './UserModule';",
"import type { User, UserRole } from './types/User';",
"import { UserService, type User, type UserRole } from './UserModule';",
"import type { ApiResponse } from './types/Api';",
"import { createUser, type CreateUserRequest } from './UserService';"
];
imports.forEach((importStatement, index) => {
const analysis = analyzer.analyzeImport(
importStatement,
`./module${index}`,
'./UserModule'
);
console.log(`๐ Analysis ${index + 1}:`, analysis);
});
// ๐ Generate optimization report
const report = analyzer.generateOptimizationReport();
console.log('๐ Optimization Report:', report);
// ๐๏ธ Generate optimized imports
const optimizedImports = analyzer.generateOptimizedImports('./module0');
console.log('๐๏ธ Optimized Imports:', optimizedImports);
};
// ๐ฏ Advanced type-only import patterns
interface TypeImportConfig {
// ๐ Module organization
typeModules: string[]; // Pure type modules
mixedModules: string[]; // Modules with types and values
valueModules: string[]; // Pure value modules
// ๐ง Import strategies
preferTypeOnly: boolean; // Prefer type-only imports when possible
separateTypeImports: boolean; // Separate type and value imports
groupImportsByModule: boolean; // Group imports from same module
// ๐ Bundle optimization
analyzeBundleImpact: boolean; // Analyze bundle size impact
generateSourceMaps: boolean; // Generate source maps for debugging
trackUnusedTypes: boolean; // Track unused type imports
}
// ๐๏ธ Smart type import manager
class SmartTypeImportManager {
private config: TypeImportConfig;
private usageTracker = new Map<string, {
imported: number;
used: number;
lastUsed: Date;
}>();
constructor(config: TypeImportConfig) {
this.config = config;
}
// ๐ฏ Optimize import statement
optimizeImportStatement(
importStatement: string,
fromModule: string,
targetModule: string
): {
optimized: string[];
bundleSizeReduction: number;
recommendations: string[];
} {
console.log(`๐ฏ Optimizing import: ${importStatement}`);
const recommendations: string[] = [];
let bundleSizeReduction = 0;
// ๐ Parse import statement
const { types, values, isTypeOnly, isMixed } = this.parseImportStatement(importStatement);
const optimized: string[] = [];
if (isMixed && this.config.separateTypeImports) {
// ๐ Separate type and value imports
if (types.length > 0) {
optimized.push(`import type { ${types.join(', ')} } from '${targetModule}';`);
bundleSizeReduction += types.length * 0.1; // Estimated reduction
}
if (values.length > 0) {
optimized.push(`import { ${values.join(', ')} } from '${targetModule}';`);
}
recommendations.push('Separated type and value imports for better tree shaking');
} else if (!isTypeOnly && types.length > 0 && this.config.preferTypeOnly) {
// ๐ Convert to type-only if all imports are types
if (values.length === 0) {
optimized.push(`import type { ${types.join(', ')} } from '${targetModule}';`);
bundleSizeReduction += types.length * 0.15;
recommendations.push('Converted to type-only import');
} else {
optimized.push(importStatement);
recommendations.push('Mixed import detected - consider separating types and values');
}
} else {
// โ
Already optimized
optimized.push(importStatement);
recommendations.push('Import statement is already optimized');
}
// ๐ Track usage
this.trackImportUsage(types.concat(values), targetModule);
return {
optimized,
bundleSizeReduction: Math.round(bundleSizeReduction * 100) / 100,
recommendations
};
}
// ๐ Parse import statement
private parseImportStatement(importStatement: string): {
types: string[];
values: string[];
isTypeOnly: boolean;
isMixed: boolean;
} {
const types: string[] = [];
const values: string[] = [];
const isTypeOnly = importStatement.includes('import type');
const hasTypeKeyword = importStatement.includes('type ');
// ๐ Simple parsing (in real implementation, use proper AST parsing)
const importMatch = importStatement.match(/import\s+(?:type\s+)?{([^}]+)}/);
if (importMatch) {
const imports = importMatch[1].split(',').map(imp => imp.trim());
imports.forEach(imp => {
if (imp.startsWith('type ')) {
types.push(imp.replace('type ', ''));
} else if (isTypeOnly) {
types.push(imp);
} else {
// ๐ค Heuristic: assume PascalCase imports are types
if (/^[A-Z]/.test(imp) && !imp.includes('Service') && !imp.includes('Manager')) {
types.push(imp);
} else {
values.push(imp);
}
}
});
}
const isMixed = types.length > 0 && values.length > 0;
return { types, values, isTypeOnly, isMixed };
}
// ๐ Track import usage
private trackImportUsage(imports: string[], fromModule: string): void {
if (!this.config.trackUnusedTypes) return;
imports.forEach(imp => {
const key = `${fromModule}:${imp}`;
const current = this.usageTracker.get(key) || { imported: 0, used: 0, lastUsed: new Date() };
current.imported++;
this.usageTracker.set(key, current);
});
}
// ๐๏ธ Generate module organization suggestions
generateModuleOrganizationSuggestions(): {
typeOnlyModules: string[];
mixedModules: string[];
organizationTips: string[];
} {
const typeOnlyModules: string[] = [];
const mixedModules: string[] = [];
const organizationTips: string[] = [];
// ๐ Analyze current module usage
const moduleStats = new Map<string, { types: number; values: number }>();
this.usageTracker.forEach((usage, key) => {
const [module, imp] = key.split(':');
const stats = moduleStats.get(module) || { types: 0, values: 0 };
// ๐ค Heuristic classification
if (/^[A-Z]/.test(imp) || imp.includes('Type') || imp.includes('Interface')) {
stats.types++;
} else {
stats.values++;
}
moduleStats.set(module, stats);
});
// ๐๏ธ Generate suggestions
moduleStats.forEach((stats, module) => {
if (stats.types > 0 && stats.values === 0) {
typeOnlyModules.push(module);
} else if (stats.types > 0 && stats.values > 0) {
mixedModules.push(module);
if (stats.types > stats.values * 2) {
organizationTips.push(
`Consider splitting ${module} - it exports mostly types`
);
}
}
});
// ๐ General organization tips
organizationTips.push(
'Create dedicated type modules for better organization',
'Use barrel exports to organize type exports',
'Separate types from runtime code when possible'
);
return {
typeOnlyModules,
mixedModules,
organizationTips
};
}
// ๐ Find unused type imports
findUnusedTypeImports(): Array<{
module: string;
import: string;
timeSinceLastUse: number;
}> {
const unused: Array<any> = [];
const now = new Date();
this.usageTracker.forEach((usage, key) => {
if (usage.used === 0) {
const [module, imp] = key.split(':');
const timeSinceLastUse = now.getTime() - usage.lastUsed.getTime();
unused.push({
module,
import: imp,
timeSinceLastUse: Math.floor(timeSinceLastUse / (1000 * 60 * 60 * 24)) // Days
});
}
});
return unused.sort((a, b) => b.timeSinceLastUse - a.timeSinceLastUse);
}
// ๐ Generate comprehensive report
generateComprehensiveReport(): {
optimizationOpportunities: number;
bundleSizeReduction: number;
unusedImports: number;
recommendations: string[];
} {
let optimizationOpportunities = 0;
let bundleSizeReduction = 0;
let unusedImports = 0;
const recommendations: string[] = [];
// ๐ Analyze usage patterns
const moduleStats = new Map<string, { types: number; values: number; mixed: number }>();
this.usageTracker.forEach((usage, key) => {
const [module] = key.split(':');
const stats = moduleStats.get(module) || { types: 0, values: 0, mixed: 0 };
if (usage.used === 0) {
unusedImports++;
bundleSizeReduction += 0.05; // Estimated savings from removing unused import
}
moduleStats.set(module, stats);
});
// ๐ฏ Count optimization opportunities
moduleStats.forEach((stats, module) => {
if (stats.types > 5 && stats.values === 0) {
optimizationOpportunities++;
recommendations.push(`${module} can be converted to type-only imports`);
}
if (stats.mixed > 3) {
optimizationOpportunities++;
recommendations.push(`${module} has mixed imports that can be separated`);
}
});
// ๐ก General recommendations
if (unusedImports > 0) {
recommendations.push(`Remove ${unusedImports} unused type imports`);
}
if (optimizationOpportunities > 0) {
recommendations.push('Implement type-only imports for better performance');
}
return {
optimizationOpportunities,
bundleSizeReduction: Math.round(bundleSizeReduction * 100) / 100,
unusedImports,
recommendations
};
}
}
// ๐ฎ Advanced usage example
const advancedTypeImportDemo = (): void => {
const config: TypeImportConfig = {
typeModules: ['./types/', './interfaces/', './models/'],
mixedModules: ['./services/', './utils/'],
valueModules: ['./constants/', './config/'],
preferTypeOnly: true,
separateTypeImports: true,
groupImportsByModule: true,
analyzeBundleImpact: true,
generateSourceMaps: true,
trackUnusedTypes: true
};
const manager = new SmartTypeImportManager(config);
// ๐ฏ Optimize various import statements
const testImports = [
"import { User, UserService, validateUser } from './services/UserService';",
"import { ApiResponse, ApiError, fetchData } from './utils/api';",
"import { Theme, Config, initializeApp } from './config/app';"
];
testImports.forEach((importStatement, index) => {
const result = manager.optimizeImportStatement(
importStatement,
`./component${index}`,
'./services/UserService'
);
console.log(`๐ฏ Optimization ${index + 1}:`, result);
});
// ๐๏ธ Get organization suggestions
const suggestions = manager.generateModuleOrganizationSuggestions();
console.log('๐๏ธ Organization Suggestions:', suggestions);
// ๐ Find unused imports
const unusedImports = manager.findUnusedTypeImports();
console.log('๐ Unused Imports:', unusedImports);
// ๐ Generate comprehensive report
const report = manager.generateComprehensiveReport();
console.log('๐ Comprehensive Report:', report);
};
// ๐ฏ Execute demonstrations
typeImportDemo();
advancedTypeImportDemo();
๐ก Common Type-Only Import Patterns
Letโs explore the most effective patterns for using type-only imports:
// ๐ Pattern 1: Pure Type Modules
// Organize types in dedicated modules for clean separation
// ๐ types/User.ts - Pure type module
export interface User {
id: string;
name: string;
email: string;
}
export interface UserPreferences {
theme: 'light' | 'dark';
language: string;
}
export type UserRole = 'admin' | 'user' | 'moderator';
export type UserStatus = 'active' | 'inactive' | 'pending';
// ๐ services/UserService.ts - Mixed module
import type { User, UserPreferences, UserRole } from '../types/User';
export class UserService {
async getUser(id: string): Promise<User> {
// Implementation
return {} as User;
}
async updatePreferences(
userId: string,
preferences: UserPreferences
): Promise<void> {
// Implementation
}
async assignRole(userId: string, role: UserRole): Promise<void> {
// Implementation
}
}
export const validateUser = (user: User): boolean => {
return user.id.length > 0 && user.email.includes('@');
};
// ๐ components/UserProfile.tsx - Consumer module
import type { User, UserPreferences } from '../types/User';
import { UserService, validateUser } from '../services/UserService';
// Clean separation: types imported as types, values as values
// ๐ Pattern 2: Conditional Type-Only Imports
// Import types only when needed for specific features
// ๐ features/AdminPanel.tsx
import type { User } from '../types/User';
// Conditionally import admin-specific types
let AdminUser: typeof import('../types/Admin').AdminUser;
let AdminPermissions: typeof import('../types/Admin').AdminPermissions;
const AdminPanel: React.FC = () => {
// Only load admin types if user is admin
const loadAdminTypes = async () => {
if (currentUser.role === 'admin') {
const adminTypes = await import('../types/Admin');
AdminUser = adminTypes.AdminUser;
AdminPermissions = adminTypes.AdminPermissions;
}
};
return <div>Admin Panel</div>;
};
// ๐ Pattern 3: Generic Type Utilities
// Create reusable type-only utilities
// ๐ types/ApiTypes.ts - Generic API type utilities
export interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
export interface ApiError {
code: string;
message: string;
details?: Record<string, any>;
}
export interface PaginatedResponse<T> {
items: T[];
totalCount: number;
page: number;
pageSize: number;
}
// Type utilities
export type ApiResult<T> = ApiResponse<T> | ApiError;
export type PaginatedApiResponse<T> = ApiResponse<PaginatedResponse<T>>;
// ๐ services/ApiService.ts - Service using type utilities
import type {
ApiResponse,
ApiError,
PaginatedResponse,
ApiResult
} from '../types/ApiTypes';
export class ApiService {
async get<T>(url: string): Promise<ApiResult<T>> {
// Implementation
return {} as ApiResult<T>;
}
async getPaginated<T>(
url: string,
page: number,
pageSize: number
): Promise<ApiResult<PaginatedResponse<T>>> {
// Implementation
return {} as ApiResult<PaginatedResponse<T>>;
}
}
// ๐ Pattern 4: Barrel Exports with Type Separation
// Organize exports to clearly separate types and values
// ๐ index.ts - Main barrel export
// Type-only exports
export type {
User,
UserPreferences,
UserRole,
UserStatus
} from './types/User';
export type {
ApiResponse,
ApiError,
PaginatedResponse
} from './types/ApiTypes';
// Value exports
export { UserService, validateUser } from './services/UserService';
export { ApiService } from './services/ApiService';
// ๐ Alternative organized barrel export
// Group by feature
export type * as UserTypes from './types/User';
export type * as ApiTypes from './types/ApiTypes';
export * as UserServices from './services/UserService';
export * as ApiServices from './services/ApiService';
// Usage with organized imports
import type { UserTypes, ApiTypes } from './index';
import { UserServices, ApiServices } from './index';
// ๐ Pattern 5: Framework-Specific Type Patterns
// Optimize for specific frameworks and libraries
// ๐ React-specific type patterns
import type { FC, PropsWithChildren, ComponentProps } from 'react';
import type { User } from '../types/User';
// Component props with type-only imports
interface UserCardProps {
user: User;
onClick?: (user: User) => void;
}
export const UserCard: FC<UserCardProps> = ({ user, onClick }) => {
return (
<div onClick={() => onClick?.(user)}>
{user.name}
</div>
);
};
// Higher-order component types
export type WithUserProps<T = {}> = T & {
user: User;
};
export const withUser = <P extends {}>(
Component: FC<P>
): FC<WithUserProps<P>> => {
return (props) => {
// Implementation
return <Component {...props} />;
};
};
// ๐ Node.js-specific type patterns
import type { Request, Response, NextFunction } from 'express';
import type { User, UserRole } from '../types/User';
// Middleware types
export type AuthenticatedRequest = Request & {
user: User;
};
export type RoleCheckMiddleware = (
roles: UserRole[]
) => (req: Request, res: Response, next: NextFunction) => void;
// API handler types
export type ApiHandler<T = any> = (
req: AuthenticatedRequest,
res: Response
) => Promise<T>;
// ๐ Pattern 6: Build-Time Type Optimization
// Optimize types for different build environments
// ๐ types/BuildOptimized.ts
// Development-only types (stripped in production)
export interface DebugInfo {
timestamp: number;
sourceLocation: string;
stackTrace: string[];
}
export interface DevToolsAPI {
log: (message: string, data?: any) => void;
inspect: (object: any) => void;
}
// Production-optimized types
export interface ProductionConfig {
apiUrl: string;
cdnUrl: string;
features: string[];
}
// Conditional type exports based on environment
export type AppConfig = typeof process.env.NODE_ENV extends 'development'
? ProductionConfig & { debug: DebugInfo; devTools: DevToolsAPI }
: ProductionConfig;
// ๐ config/environment.ts
import type { AppConfig } from '../types/BuildOptimized';
// Only import debug types in development
let DebugInfo: typeof import('../types/BuildOptimized').DebugInfo;
let DevToolsAPI: typeof import('../types/BuildOptimized').DevToolsAPI;
if (process.env.NODE_ENV === 'development') {
// These imports are tree-shaken in production
import('../types/BuildOptimized').then(module => {
DebugInfo = module.DebugInfo;
DevToolsAPI = module.DevToolsAPI;
});
}
export const createConfig = (): AppConfig => {
const baseConfig = {
apiUrl: process.env.API_URL || '',
cdnUrl: process.env.CDN_URL || '',
features: []
};
if (process.env.NODE_ENV === 'development') {
return {
...baseConfig,
debug: {
timestamp: Date.now(),
sourceLocation: __filename,
stackTrace: []
},
devTools: {
log: console.log,
inspect: console.dir
}
} as AppConfig;
}
return baseConfig as AppConfig;
};
// ๐ Pattern 7: Micro-Frontend Type Sharing
// Share types across micro-frontends efficiently
// ๐ shared-types/package.json
{
"name": "@company/shared-types",
"version": "1.0.0",
"types": "./dist/index.d.ts",
"sideEffects": false // Important for tree-shaking
}
// ๐ shared-types/src/index.ts
// Only export types - no runtime code
export type { User, UserRole, UserPreferences } from './User';
export type { ApiResponse, ApiError } from './Api';
export type { Theme, Config } from './Common';
// Type utilities
export type * as UserTypes from './User';
export type * as ApiTypes from './Api';
export type * as CommonTypes from './Common';
// ๐ micro-frontend-a/src/components/UserList.tsx
import type { User } from '@company/shared-types';
// No runtime dependencies on shared package!
// ๐ micro-frontend-b/src/services/UserService.ts
import type { User, ApiResponse } from '@company/shared-types';
// Consistent types across micro-frontends
// ๐ Pattern 8: Library Type Definitions
// Create efficient type definitions for libraries
// ๐ my-library/types/index.ts
// Library public API types
export interface LibraryConfig {
apiKey: string;
baseUrl?: string;
timeout?: number;
}
export interface LibraryInstance {
configure: (config: LibraryConfig) => void;
request: <T>(options: RequestOptions) => Promise<T>;
}
// Internal types (not exported)
interface InternalState {
initialized: boolean;
config: LibraryConfig;
}
// Type guards for runtime type checking
export const isLibraryConfig = (obj: any): obj is LibraryConfig => {
return obj && typeof obj.apiKey === 'string';
};
// ๐ my-library/src/index.ts
import type { LibraryConfig, LibraryInstance } from '../types';
export type { LibraryConfig, LibraryInstance } from '../types';
export const createLibrary = (config: LibraryConfig): LibraryInstance => {
// Implementation
return {} as LibraryInstance;
};
// Users only get type imports, minimal runtime footprint
// Usage:
// import type { LibraryConfig } from 'my-library';
// import { createLibrary } from 'my-library';
๐ Conclusion
Congratulations! Youโve mastered the art of type-only imports! ๐ญ
๐ฏ What Youโve Learned
- ๐ญ Type-Only Syntax: Using
import type
andtype
keywords effectively - ๐ฆ Bundle Optimization: Eliminating unnecessary runtime imports for smaller bundles
- ๐๏ธ Clean Architecture: Separating compile-time types from runtime values
- ๐ง Advanced Patterns: Sophisticated strategies for organizing and optimizing type imports
- ๐ Performance Analysis: Tools and techniques for measuring and improving import efficiency
๐ Key Benefits
- ๐ Smaller Bundles: Eliminate unused runtime code from your bundles
- โก Faster Builds: Reduce compilation overhead with optimized imports
- ๐ฏ Better Organization: Clear separation between types and runtime code
- ๐ง Easier Refactoring: Type-only imports make refactoring safer and more predictable
- ๐ฆ Library Optimization: Create more efficient libraries with minimal runtime footprint
๐ฅ Best Practices Recap
- ๐ฏ Separate Types and Values: Use type-only imports for types, regular imports for values
- ๐ Organize by Purpose: Create dedicated type modules for better organization
- ๐ง Leverage Tools: Use analyzers to find optimization opportunities
- ๐ Monitor Bundle Size: Track the impact of your import optimizations
- ๐ญ Think at Design Time: Plan your module architecture with type imports in mind
Youโre now equipped to create highly optimized TypeScript applications with clean, efficient module architectures that maximize performance while maintaining excellent type safety! ๐
Happy coding, and may your types always be imported exactly when needed! ๐ญโจ