TypeScript Standards
Type safety standards and best practices for the Robota SDK v2.0.
Zero Any/Unknown Policy
The Robota SDK v2.0 enforces a Zero Any/Unknown Policy - complete elimination of any
and unsafe unknown
types for maximum type safety.
Policy Enforcement
typescript
// ✅ Good: Specific types
interface AgentConfig {
name: string;
model: string;
provider: 'openai' | 'anthropic' | 'google';
aiProviders: Record<string, BaseAIProvider>;
systemMessage?: string;
tools?: Tool[];
plugins?: BasePlugin[];
}
// ❌ Bad: Any types (not allowed)
interface BadConfig {
providers: any; // Never use any
options: any; // Always type explicitly
data: unknown; // Prefer specific types
}
// ✅ Good: Type assertions with guards
function processResponse(response: unknown): string {
if (typeof response === 'string') {
return response;
}
throw new Error('Invalid response type');
}
TypeScript Configuration
Strict Configuration (tsconfig.json)
json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true
}
}
ESLint Rules for Type Safety
json
{
"rules": {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unsafe-any": "error",
"@typescript-eslint/no-unsafe-assignment": "error",
"@typescript-eslint/no-unsafe-call": "error",
"@typescript-eslint/no-unsafe-member-access": "error",
"@typescript-eslint/no-unsafe-return": "error",
"@typescript-eslint/prefer-unknown-to-any": "error"
}
}
Type Definition Standards
Interface Design
typescript
// ✅ Good: Comprehensive interface with readonly properties
interface AgentStats {
readonly name: string;
readonly uptime: number;
readonly historyLength: number;
readonly providerStats: Readonly<Record<string, ProviderStats>>;
readonly lastInteraction?: Readonly<Date>;
}
// ✅ Good: Branded types for type safety
type ModelName = string & { readonly __brand: 'ModelName' };
type ProviderName = string & { readonly __brand: 'ProviderName' };
type AgentName = string & { readonly __brand: 'AgentName' };
// Helper functions for branded types
function createModelName(value: string): ModelName {
return value as ModelName;
}
// ✅ Good: Discriminated unions
type ToolResult =
| { success: true; data: unknown; metadata?: Record<string, unknown> }
| { success: false; error: string; code?: string };
type StreamChunk =
| { type: 'content'; content: string }
| { type: 'error'; error: string }
| { type: 'end' };
Generic Type Patterns
typescript
// ✅ Good: Proper generic constraints
interface BaseAgent<TStats extends AgentStats = AgentStats> {
getStats(): TStats;
run(input: string): Promise<string>;
stream(input: string): AsyncIterable<StreamChunk>;
destroy(): Promise<void>;
}
// ✅ Good: Conditional types for flexibility
type PluginConfig<T extends BasePlugin> = T extends ExecutionAnalyticsPlugin
? ExecutionAnalyticsConfig
: T extends ConversationHistoryPlugin
? ConversationHistoryConfig
: T extends LoggingPlugin
? LoggingConfig
: never;
// ✅ Good: Mapped types for transformations
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
type RequiredFields<T, K extends keyof T> = T & Required<Pick<T, K>>;
// Usage examples
type PartialAgentConfig = Optional<AgentConfig, 'systemMessage' | 'tools'>;
type RequiredProviderConfig = RequiredFields<ProviderConfig, 'apiKey'>;
Error Type Safety
Result Pattern
typescript
// ✅ Good: Type-safe result pattern
type Result<T, E extends Error = Error> =
| { success: true; data: T }
| { success: false; error: E };
// Custom error types
class AgentError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly context?: Record<string, unknown>
) {
super(message);
this.name = 'AgentError';
}
}
class ProviderError extends AgentError {
constructor(
message: string,
public readonly provider: ProviderName,
context?: Record<string, unknown>
) {
super(message, 'PROVIDER_ERROR', { ...context, provider });
this.name = 'ProviderError';
}
}
// Usage
async function safeAgentRun(agent: Robota, input: string): Promise<Result<string, AgentError>> {
try {
const result = await agent.run(input);
return { success: true, data: result };
} catch (error) {
if (error instanceof AgentError) {
return { success: false, error };
}
// Convert unknown errors to AgentError
return {
success: false,
error: new AgentError('Unknown error', 'UNKNOWN_ERROR', { originalError: error })
};
}
}
Option Pattern
typescript
// ✅ Good: Option pattern for nullable values
type Option<T> = { some: T } | { none: true };
function some<T>(value: T): Option<T> {
return { some: value };
}
function none<T>(): Option<T> {
return { none: true };
}
function isSome<T>(option: Option<T>): option is { some: T } {
return 'some' in option;
}
// Usage
function findPlugin(agent: Robota, name: string): Option<BasePlugin> {
const plugin = agent.getPlugin(name);
return plugin ? some(plugin) : none();
}
Plugin Type System
Plugin Interface
typescript
// ✅ Good: Generic plugin interface
export abstract class BasePlugin<TStats extends PluginStats = PluginStats> {
abstract readonly name: string;
abstract getStats(): TStats;
// Optional lifecycle methods
onAgentStart?(): Promise<void>;
onAgentStop?(): Promise<void>;
onAgentRun?(input: string): Promise<void>;
onAgentResponse?(response: string): Promise<void>;
}
// Specific plugin stats
interface ExecutionAnalyticsStats extends PluginStats {
readonly totalExecutions: number;
readonly successRate: number;
readonly averageDuration: number;
readonly errorCount: number;
}
interface ConversationHistoryStats extends PluginStats {
readonly messageCount: number;
readonly oldestMessage?: Date;
readonly newestMessage?: Date;
}
// Plugin implementations
export class ExecutionAnalyticsPlugin extends BasePlugin<ExecutionAnalyticsStats> {
readonly name = 'ExecutionAnalyticsPlugin';
getStats(): ExecutionAnalyticsStats {
return {
name: this.name,
totalExecutions: this.totalExecutions,
successRate: this.calculateSuccessRate(),
averageDuration: this.calculateAverageDuration(),
errorCount: this.errorCount
};
}
}
Tool Type System
Tool Interface
typescript
// ✅ Good: Type-safe tool definition
interface ToolSchema {
name: string;
description: string;
parameters: JSONSchema;
handler: (params: unknown) => Promise<unknown>;
}
// Type-safe tool creation
function createFunctionTool<TParams, TResult>(
name: string,
description: string,
schema: JSONSchema,
handler: (params: TParams) => Promise<TResult>
): Tool<TParams, TResult> {
return {
name,
description,
schema,
execute: handler
};
}
// Usage with full type safety
const calculatorTool = createFunctionTool(
'calculate',
'Performs mathematical calculations',
{
type: 'object',
properties: {
operation: { type: 'string', enum: ['add', 'subtract', 'multiply', 'divide'] },
a: { type: 'number' },
b: { type: 'number' }
},
required: ['operation', 'a', 'b']
} as const,
async (params: { operation: 'add' | 'subtract' | 'multiply' | 'divide'; a: number; b: number }) => {
switch (params.operation) {
case 'add': return { result: params.a + params.b };
case 'subtract': return { result: params.a - params.b };
case 'multiply': return { result: params.a * params.b };
case 'divide': return { result: params.a / params.b };
}
}
);
Provider Type System
Provider Interface
typescript
// ✅ Good: Provider type system
export abstract class BaseAIProvider<TOptions extends ProviderOptions = ProviderOptions> {
constructor(protected readonly options: TOptions) {}
abstract generateResponse(
messages: UniversalMessage[],
options?: GenerationOptions
): Promise<string>;
abstract generateStream(
messages: UniversalMessage[],
options?: GenerationOptions
): AsyncIterable<StreamChunk>;
abstract getSupportedModels(): readonly ModelName[];
abstract validateModel(model: string): model is ModelName;
}
// Provider-specific types
interface OpenAIProviderOptions extends ProviderOptions {
readonly client: OpenAI;
readonly organization?: string;
}
interface AnthropicProviderOptions extends ProviderOptions {
readonly client: Anthropic;
readonly version?: string;
}
// Provider implementations
export class OpenAIProvider extends BaseAIProvider<OpenAIProviderOptions> {
getSupportedModels(): readonly ModelName[] {
return [
createModelName('gpt-3.5-turbo'),
createModelName('gpt-4'),
createModelName('gpt-4o-mini')
] as const;
}
validateModel(model: string): model is ModelName {
return this.getSupportedModels().includes(model as ModelName);
}
}
Type Documentation
JSDoc Integration
typescript
/**
* Configuration for creating a Robota agent
* @template TStats - The agent statistics type
*/
interface AgentConfig<TStats extends AgentStats = AgentStats> {
/** Unique identifier for the agent */
readonly name: AgentName;
/** AI model to use */
readonly model: ModelName;
/** Provider identifier */
readonly provider: ProviderName;
/**
* Available AI providers
* @example
* ```typescript
* {
* openai: new OpenAIProvider({ client: openaiClient }),
* anthropic: new AnthropicProvider({ client: anthropicClient })
* }
* ```
*/
readonly aiProviders: Record<string, BaseAIProvider>;
/** System message for the agent */
readonly systemMessage?: string;
/** Available tools for the agent */
readonly tools?: readonly Tool[];
/** Plugins to extend agent functionality */
readonly plugins?: readonly BasePlugin[];
}
Type Utility Documentation
typescript
/**
* Utility types for common patterns in the Robota SDK
*/
/** Make specific properties optional */
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
/** Make specific properties required */
type RequiredFields<T, K extends keyof T> = T & Required<Pick<T, K>>;
/** Deep readonly type */
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
/** Extract function parameter types */
type ParametersOf<T> = T extends (...args: infer P) => unknown ? P : never;
/** Extract function return type */
type ReturnTypeOf<T> = T extends (...args: unknown[]) => infer R ? R : never;
Best Practices Summary
✅ Do
- Use specific types: Always prefer specific types over
any
orunknown
- Leverage generics: Use type parameters for reusable components
- Document types: Add JSDoc comments for complex types
- Use branded types: For domain-specific values like IDs
- Implement type guards: For runtime type checking
- Use readonly: For immutable data structures
- Prefer interfaces: Over type aliases for object shapes
❌ Don't
- Use
any
types: Completely forbidden in the codebase - Use
unknown
unsafely: Always use type guards - Ignore TypeScript errors: Fix all type errors
- Use function overloads: Prefer union types
- Mutate readonly data: Respect immutability
- Use index signatures: Prefer specific property definitions
Migration from v1.x
Type System Changes
typescript
// v1.x (deprecated)
interface OldConfig {
providers: any; // ❌ Any types
options: unknown; // ❌ Unsafe unknown
}
// v2.0 (current)
interface NewConfig {
providers: Record<string, BaseAIProvider>; // ✅ Specific types
options: GenerationOptions; // ✅ Well-defined interface
}
This type system ensures complete type safety and excellent developer experience throughout the Robota SDK.