This example demonstrates multiple levels of interface inheritance, showing how to build a layered architecture.
Framework (Core)
↓ extends
Platform (Auth & Users)
↓ extends
Application (Orders)
03-multi-level-extension/
├── framework.define.ts # Layer 1: Core framework
├── platform.define.ts # Layer 2: Platform (extends framework)
└── define.ts # Layer 3: Application (extends platform)
FrameworkServerFunctions
└─ healthCheck()
PlatformServerFunctions extends FrameworkServerFunctions
├─ healthCheck() (inherited)
├─ login()
├─ getCurrentUser()
└─ logout()
ServerFunctions extends PlatformServerFunctions
├─ healthCheck() (inherited)
├─ login() (inherited)
├─ getCurrentUser() (inherited)
├─ logout() (inherited)
├─ getOrder()
├─ createOrder()
├─ cancelOrder()
└─ listOrders()
FrameworkClientFunctions
├─ log()
└─ getVersion()
PlatformClientFunctions extends FrameworkClientFunctions
├─ log() (inherited)
├─ getVersion() (inherited)
├─ showNotification()
└─ requestAuth()
ClientFunctions extends PlatformClientFunctions
├─ log() (inherited)
├─ getVersion() (inherited)
├─ showNotification() (inherited)
├─ requestAuth() (inherited)
├─ onOrderStatusChanged()
├─ refreshOrderList()
└─ confirmPayment()
bun run ../../index.ts ./define.tsThe generator will:
- Read
define.ts(Application layer) - Follow import to
platform.define.ts(Platform layer) - Follow import to
framework.define.ts(Framework layer) - Resolve the complete inheritance chain
- Generate code for all 8 server functions and all 7 client functions
After generation, you'll have:
Client->Server calls (8 functions total):
- Framework layer (1):
healthCheck() - Platform layer (3):
login(),getCurrentUser(),logout() - Application layer (4):
getOrder(),createOrder(),cancelOrder(),listOrders()
Server->Client calls (7 functions total):
- Framework layer (2):
log(),getVersion() - Platform layer (2):
showNotification(),requestAuth() - Application layer (3):
onOrderStatusChanged(),refreshOrderList(),confirmPayment()
import { io } from 'socket.io-client';
import {
// Framework layer
healthCheck,
// Platform layer
login,
getCurrentUser,
// Application layer
createOrder,
getOrder,
// Handlers
handleLog,
handleShowNotification,
handleOnOrderStatusChanged
} from './client.generated';
const socket = io('http://localhost:3000');
// Use functions from any layer
const health = await healthCheck(socket); // Framework
const { user, token } = await login(socket, 'john', 'pass123'); // Platform
const order = await createOrder(socket, { items: ['item1'] }); // Application
// Set up handlers from any layer
handleLog(socket, async (socket, level, message) => {
console[level](message); // Framework
});
handleShowNotification(socket, async (socket, title, message) => {
alert(`${title}: ${message}`); // Platform
});
handleOnOrderStatusChanged(socket, async (socket, update) => {
console.log('Order updated:', update); // Application
});This pattern is useful for:
- Framework Developers: Provide base RPC functions that all applications use
- Platform Teams: Add platform-specific functions (auth, monitoring, etc.)
- Application Developers: Focus on business logic without reimplementing common functionality
- Microservices: Share common interface layers across services
- Plugins/Extensions: Allow third parties to extend your RPC interfaces
- Layered Architecture: Clear separation of concerns
- Code Reuse: Framework and platform functions available everywhere
- Type Safety: Full TypeScript inference across all layers
- Maintainability: Update lower layers without touching applications
- Scalability: Add new layers as your system grows