# 🎯 Типизированные элементы в CSElement

Типизированные элементы обеспечивают строгую типизацию данных, валидацию и автодополнение. Они превращают динамические структуры в надежные, типобезопасные компоненты.

## 🎯 Основы типизации

### Создание схемы типа

```typescript
import { CSElement, TypedElementSchema } from 'cs-element';

// Определяем схему пользователя
const UserSchema: TypedElementSchema<User> = {
  name: 'User',
  version: '1.0.0',
  abstract: false,
  sealed: false,
  fields: {
    id: {
      name: 'id',
      type: 'string',
      required: true,
      unique: true,
      validation: {
        pattern: /^user_\d+$/
      }
    },
    email: {
      name: 'email',
      type: 'string',
      required: true,
      unique: true,
      validation: {
        pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
      }
    },
    name: {
      name: 'name',
      type: 'string',
      required: true,
      minLength: 2,
      maxLength: 50
    },
    age: {
      name: 'age',
      type: 'number',
      required: false,
      min: 0,
      max: 150
    },
    role: {
      name: 'role',
      type: 'string',
      required: true,
      enum: ['admin', 'user', 'moderator'],
      defaultValue: 'user'
    },
    isActive: {
      name: 'isActive',
      type: 'boolean',
      required: true,
      defaultValue: true
    },
    metadata: {
      name: 'metadata',
      type: 'object',
      required: false,
      defaultValue: {}
    }
  }
};

interface User {
  id: string;
  email: string;
  name: string;
  age?: number;
  role: 'admin' | 'user' | 'moderator';
  isActive: boolean;
  metadata?: Record<string, any>;
}
```

### Создание типизированных элементов

```typescript
// Регистрируем схему в менеджере
const typedManager = CSElement.typedElements;
typedManager.registerSchema(UserSchema);

// Создаем типизированный элемент
const user = await typedManager.createElement<User>('User', {
  id: 'user_001',
  email: 'alice@example.com',
  name: 'Alice Johnson',
  age: 28,
  role: 'admin'
});

console.log('Создан пользователь:', user.getTypedData());
// Типизированный доступ к данным
console.log('Email:', user.getField('email')); // Автодополнение работает!
console.log('Роль:', user.getField('role'));
```

## 🏗️ Наследование типов

### Базовые и производные типы

```typescript
// Базовая схема сотрудника
const EmployeeSchema: TypedElementSchema<Employee> = {
  name: 'Employee',
  version: '1.0.0',
  abstract: true, // Абстрактный тип
  fields: {
    employeeId: {
      name: 'employeeId',
      type: 'string',
      required: true,
      unique: true
    },
    firstName: {
      name: 'firstName',
      type: 'string',
      required: true
    },
    lastName: {
      name: 'lastName',
      type: 'string',
      required: true
    },
    department: {
      name: 'department',
      type: 'string',
      required: true
    },
    salary: {
      name: 'salary',
      type: 'number',
      required: true,
      min: 0
    }
  }
};

// Схема разработчика (наследует от Employee)
const DeveloperSchema: TypedElementSchema<Developer> = {
  name: 'Developer',
  version: '1.0.0',
  extends: 'Employee', // Наследование
  fields: {
    programmingLanguages: {
      name: 'programmingLanguages',
      type: 'array',
      required: true,
      defaultValue: []
    },
    experience: {
      name: 'experience',
      type: 'number',
      required: true,
      min: 0
    },
    seniority: {
      name: 'seniority',
      type: 'string',
      required: true,
      enum: ['junior', 'middle', 'senior', 'lead']
    }
  }
};

// Схема менеджера (тоже наследует от Employee)
const ManagerSchema: TypedElementSchema<Manager> = {
  name: 'Manager',
  version: '1.0.0',
  extends: 'Employee',
  fields: {
    teamSize: {
      name: 'teamSize',
      type: 'number',
      required: true,
      min: 1
    },
    budget: {
      name: 'budget',
      type: 'number',
      required: false,
      min: 0
    }
  }
};

interface Employee {
  employeeId: string;
  firstName: string;
  lastName: string;
  department: string;
  salary: number;
}

interface Developer extends Employee {
  programmingLanguages: string[];
  experience: number;
  seniority: 'junior' | 'middle' | 'senior' | 'lead';
}

interface Manager extends Employee {
  teamSize: number;
  budget?: number;
}

// Регистрируем схемы
typedManager.registerSchema(EmployeeSchema);
typedManager.registerSchema(DeveloperSchema);
typedManager.registerSchema(ManagerSchema);

// Создаем типизированных сотрудников
const developer = await typedManager.createElement<Developer>('Developer', {
  employeeId: 'DEV001',
  firstName: 'John',
  lastName: 'Doe',
  department: 'Engineering',
  salary: 80000,
  programmingLanguages: ['TypeScript', 'Python', 'Go'],
  experience: 5,
  seniority: 'senior'
});

const manager = await typedManager.createElement<Manager>('Manager', {
  employeeId: 'MGR001',
  firstName: 'Jane',
  lastName: 'Smith',
  department: 'Engineering',
  salary: 120000,
  teamSize: 8,
  budget: 500000
});

// Проверка типов
console.log('Разработчик:', developer.isInstanceOf('Developer')); // true
console.log('Сотрудник:', developer.isInstanceOf('Employee')); // true
console.log('Менеджер:', developer.isInstanceOf('Manager')); // false
```

## 🔍 Валидация и проверки

### Автоматическая валидация

```typescript
// Попытка создать невалидного пользователя
try {
  const invalidUser = await typedManager.createElement<User>('User', {
    id: 'invalid_id', // Не соответствует паттерну
    email: 'not-an-email', // Невалидный email
    name: 'A', // Слишком короткое имя
    age: -5, // Отрицательный возраст
    role: 'superadmin' // Недопустимая роль
  });
} catch (error) {
  console.log('Ошибки валидации:', error.validationErrors);
  // Выведет подробные ошибки по каждому полю
}

// Валидация отдельных полей
const user = await typedManager.createElement<User>('User', {
  id: 'user_002',
  email: 'bob@example.com',
  name: 'Bob'
});

// Проверяем конкретное поле
const emailValidation = await user.validateField('email', 'invalid-email');
console.log('Email валиден:', emailValidation.isValid);
console.log('Ошибки email:', emailValidation.errors);

// Полная валидация элемента
const fullValidation = await user.validateType();
console.log('Элемент валиден:', fullValidation.isValid);
```

### Кастомная валидация

```typescript
// Схема с кастомной валидацией
const ProductSchema: TypedElementSchema<Product> = {
  name: 'Product',
  version: '1.0.0',
  fields: {
    sku: {
      name: 'sku',
      type: 'string',
      required: true,
      validation: {
        custom: (value: string) => {
          // Кастомная логика валидации SKU
          if (!value.startsWith('PRD-')) {
            return { valid: false, message: 'SKU должен начинаться с PRD-' };
          }
          if (value.length !== 10) {
            return { valid: false, message: 'SKU должен быть длиной 10 символов' };
          }
          return { valid: true };
        }
      }
    },
    price: {
      name: 'price',
      type: 'number',
      required: true,
      validation: {
        custom: (value: number, element: any) => {
          const category = element.getField('category');
          const minPrice = category === 'premium' ? 100 : 10;
          
          if (value < minPrice) {
            return { 
              valid: false, 
              message: `Цена для категории ${category} должна быть не менее ${minPrice}` 
            };
          }
          return { valid: true };
        }
      }
    },
    category: {
      name: 'category',
      type: 'string',
      required: true,
      enum: ['basic', 'standard', 'premium']
    }
  }
};

interface Product {
  sku: string;
  price: number;
  category: 'basic' | 'standard' | 'premium';
}
```

## 🚀 Продвинутые возможности

### Типизированные коллекции

```typescript
class TypedCollection<T> extends CSElement {
  private schema: TypedElementSchema<T>;
  private typedManager: any;

  constructor(name: string, schema: TypedElementSchema<T>) {
    super(name);
    this.schema = schema;
    this.typedManager = CSElement.typedElements;
    this.typedManager.registerSchema(schema);
  }

  async addTypedElement(data: Partial<T>): Promise<TypedElement<T>> {
    const element = await this.typedManager.createElement<T>(this.schema.name, data);
    await this.addElement(element.id, { element });
    return element;
  }

  getTypedElements(): TypedElement<T>[] {
    return this.getAllElements()
      .filter(el => el instanceof TypedElement)
      .map(el => el as TypedElement<T>);
  }

  queryTyped(predicate: (item: T) => boolean): TypedElement<T>[] {
    return this.getTypedElements().filter(el => {
      const data = el.getTypedData();
      return predicate(data as T);
    });
  }

  async updateElement(id: string, updates: Partial<T>): Promise<boolean> {
    const element = this.getElement(id);
    if (element instanceof TypedElement) {
      await element.setTypedData(updates);
      return true;
    }
    return false;
  }

  validateAll(): Promise<ValidationResult[]> {
    const elements = this.getTypedElements();
    return Promise.all(elements.map(el => el.validateType()));
  }
}

// Использование типизированной коллекции
const userCollection = new TypedCollection<User>('Users', UserSchema);

// Добавляем пользователей с автоматической валидацией
const alice = await userCollection.addTypedElement({
  id: 'user_003',
  email: 'alice@company.com',
  name: 'Alice Cooper',
  role: 'admin'
});

const bob = await userCollection.addTypedElement({
  id: 'user_004',
  email: 'bob@company.com',
  name: 'Bob Wilson',
  role: 'user',
  age: 35
});

// Типизированные запросы
const admins = userCollection.queryTyped(user => user.role === 'admin');
const adults = userCollection.queryTyped(user => (user.age || 0) >= 18);

console.log('Администраторы:', admins.length);
console.log('Взрослые пользователи:', adults.length);
```

### Миграции схем

```typescript
// Версия 1.0.0 схемы пользователя
const UserSchemaV1: TypedElementSchema<UserV1> = {
  name: 'User',
  version: '1.0.0',
  fields: {
    id: { name: 'id', type: 'string', required: true },
    name: { name: 'name', type: 'string', required: true },
    email: { name: 'email', type: 'string', required: true }
  }
};

// Версия 2.0.0 - добавили новые поля
const UserSchemaV2: TypedElementSchema<UserV2> = {
  name: 'User',
  version: '2.0.0',
  fields: {
    id: { name: 'id', type: 'string', required: true },
    firstName: { name: 'firstName', type: 'string', required: true },
    lastName: { name: 'lastName', type: 'string', required: true },
    email: { name: 'email', type: 'string', required: true },
    role: { name: 'role', type: 'string', required: true, defaultValue: 'user' },
    createdAt: { name: 'createdAt', type: 'string', required: true }
  },
  migrations: {
    '1.0.0': {
      up: (oldData: UserV1) => {
        const nameParts = oldData.name.split(' ');
        return {
          id: oldData.id,
          firstName: nameParts[0] || oldData.name,
          lastName: nameParts.slice(1).join(' ') || '',
          email: oldData.email,
          role: 'user',
          createdAt: new Date().toISOString()
        };
      },
      down: (newData: UserV2) => {
        return {
          id: newData.id,
          name: `${newData.firstName} ${newData.lastName}`.trim(),
          email: newData.email
        };
      }
    }
  }
};

interface UserV1 {
  id: string;
  name: string;
  email: string;
}

interface UserV2 {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
  role: string;
  createdAt: string;
}
```

## 🎯 Полный пример - Система управления сотрудниками

```typescript
import { CSElement, TypedElementSchema } from 'cs-element';

// Определяем все схемы
const PersonSchema: TypedElementSchema<Person> = {
  name: 'Person',
  version: '1.0.0',
  abstract: true,
  fields: {
    id: { name: 'id', type: 'string', required: true, unique: true },
    firstName: { name: 'firstName', type: 'string', required: true },
    lastName: { name: 'lastName', type: 'string', required: true },
    email: { name: 'email', type: 'string', required: true, unique: true },
    phone: { name: 'phone', type: 'string', required: false },
    birthDate: { name: 'birthDate', type: 'string', required: false }
  }
};

const EmployeeSchema: TypedElementSchema<Employee> = {
  name: 'Employee',
  version: '1.0.0',
  extends: 'Person',
  fields: {
    employeeId: { name: 'employeeId', type: 'string', required: true, unique: true },
    department: { name: 'department', type: 'string', required: true },
    position: { name: 'position', type: 'string', required: true },
    salary: { name: 'salary', type: 'number', required: true, min: 0 },
    hireDate: { name: 'hireDate', type: 'string', required: true },
    isActive: { name: 'isActive', type: 'boolean', required: true, defaultValue: true }
  }
};

const DepartmentSchema: TypedElementSchema<Department> = {
  name: 'Department',
  version: '1.0.0',
  fields: {
    id: { name: 'id', type: 'string', required: true, unique: true },
    name: { name: 'name', type: 'string', required: true },
    budget: { name: 'budget', type: 'number', required: true, min: 0 },
    managerId: { name: 'managerId', type: 'string', required: false },
    employeeCount: { name: 'employeeCount', type: 'number', required: true, defaultValue: 0 }
  }
};

interface Person {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
  phone?: string;
  birthDate?: string;
}

interface Employee extends Person {
  employeeId: string;
  department: string;
  position: string;
  salary: number;
  hireDate: string;
  isActive: boolean;
}

interface Department {
  id: string;
  name: string;
  budget: number;
  managerId?: string;
  employeeCount: number;
}

class EmployeeManagementSystem extends CSElement {
  private typedManager: any;
  private employees: TypedCollection<Employee>;
  private departments: TypedCollection<Department>;

  constructor() {
    super('EmployeeManagementSystem');
    this.typedManager = CSElement.typedElements;
    this.initializeSchemas();
    this.initializeCollections();
    this.setupReactivity();
  }

  private initializeSchemas() {
    this.typedManager.registerSchema(PersonSchema);
    this.typedManager.registerSchema(EmployeeSchema);
    this.typedManager.registerSchema(DepartmentSchema);
  }

  private initializeCollections() {
    this.employees = new TypedCollection<Employee>('Employees', EmployeeSchema);
    this.departments = new TypedCollection<Department>('Departments', DepartmentSchema);
    
    this.withChild(this.employees);
    this.withChild(this.departments);
  }

  private setupReactivity() {
    // Автоматически обновляем количество сотрудников в отделах
    this.employees.on('element:added', () => this.updateDepartmentCounts());
    this.employees.on('element:removed', () => this.updateDepartmentCounts());
    this.employees.on('data:changed', (employee, key) => {
      if (key === 'department') {
        this.updateDepartmentCounts();
      }
    });
  }

  private async updateDepartmentCounts() {
    const allEmployees = this.employees.getTypedElements();
    const departmentCounts = new Map<string, number>();

    // Подсчитываем сотрудников по отделам
    allEmployees.forEach(emp => {
      const dept = emp.getField('department');
      if (dept) {
        departmentCounts.set(dept, (departmentCounts.get(dept) || 0) + 1);
      }
    });

    // Обновляем счетчики в отделах
    const allDepartments = this.departments.getTypedElements();
    for (const dept of allDepartments) {
      const deptName = dept.getField('name');
      const count = departmentCounts.get(deptName) || 0;
      await dept.setField('employeeCount', count);
    }
  }

  async addEmployee(employeeData: Omit<Employee, 'isActive'>): Promise<Employee> {
    const employee = await this.employees.addTypedElement({
      ...employeeData,
      isActive: true
    });

    console.log(`✅ Сотрудник добавлен: ${employee.getField('firstName')} ${employee.getField('lastName')}`);
    return employee;
  }

  async addDepartment(departmentData: Omit<Department, 'employeeCount'>): Promise<Department> {
    const department = await this.departments.addTypedElement({
      ...departmentData,
      employeeCount: 0
    });

    console.log(`🏢 Отдел создан: ${department.getField('name')}`);
    return department;
  }

  async promoteEmployee(employeeId: string, newPosition: string, newSalary: number): Promise<boolean> {
    const employee = this.employees.getElement(employeeId);
    if (employee instanceof TypedElement) {
      const oldPosition = employee.getField('position');
      const oldSalary = employee.getField('salary');
      
      await employee.setField('position', newPosition);
      await employee.setField('salary', newSalary);
      
      console.log(`🚀 Повышение: ${employee.getField('firstName')} ${employee.getField('lastName')}`);
      console.log(`   ${oldPosition} → ${newPosition}`);
      console.log(`   ${oldSalary}₽ → ${newSalary}₽`);
      
      return true;
    }
    return false;
  }

  async transferEmployee(employeeId: string, newDepartment: string): Promise<boolean> {
    const employee = this.employees.getElement(employeeId);
    if (employee instanceof TypedElement) {
      const oldDepartment = employee.getField('department');
      await employee.setField('department', newDepartment);
      
      console.log(`🔄 Перевод: ${employee.getField('firstName')} ${employee.getField('lastName')}`);
      console.log(`   ${oldDepartment} → ${newDepartment}`);
      
      return true;
    }
    return false;
  }

  getEmployeesByDepartment(departmentName: string): Employee[] {
    return this.employees.queryTyped(emp => emp.department === departmentName)
      .map(emp => emp.getTypedData() as Employee);
  }

  getEmployeesByPosition(position: string): Employee[] {
    return this.employees.queryTyped(emp => emp.position === position)
      .map(emp => emp.getTypedData() as Employee);
  }

  getSalaryStatistics() {
    const employees = this.employees.getTypedElements();
    const salaries = employees.map(emp => emp.getField('salary')).filter(Boolean);
    
    if (salaries.length === 0) return null;

    const total = salaries.reduce((sum, salary) => sum + salary, 0);
    const avg = total / salaries.length;
    const min = Math.min(...salaries);
    const max = Math.max(...salaries);
    
    return { total, average: avg, minimum: min, maximum: max, count: salaries.length };
  }

  getDepartmentStatistics() {
    const departments = this.departments.getTypedElements();
    return departments.map(dept => ({
      name: dept.getField('name'),
      budget: dept.getField('budget'),
      employeeCount: dept.getField('employeeCount'),
      managerId: dept.getField('managerId')
    }));
  }

  async validateAllEmployees(): Promise<{ valid: Employee[], invalid: Array<{ employee: Employee, errors: string[] }> }> {
    const employees = this.employees.getTypedElements();
    const valid: Employee[] = [];
    const invalid: Array<{ employee: Employee, errors: string[] }> = [];

    for (const emp of employees) {
      const validation = await emp.validateType();
      if (validation.isValid) {
        valid.push(emp.getTypedData() as Employee);
      } else {
        invalid.push({
          employee: emp.getTypedData() as Employee,
          errors: validation.errors.map(e => e.message)
        });
      }
    }

    return { valid, invalid };
  }

  generateReport(): string {
    const employeeCount = this.employees.elementsCount();
    const departmentCount = this.departments.elementsCount();
    const salaryStats = this.getSalaryStatistics();
    const deptStats = this.getDepartmentStatistics();

    let report = `📊 Отчет по системе управления сотрудниками\n`;
    report += `═══════════════════════════════════════════\n\n`;
    report += `👥 Общая информация:\n`;
    report += `   Всего сотрудников: ${employeeCount}\n`;
    report += `   Всего отделов: ${departmentCount}\n\n`;

    if (salaryStats) {
      report += `💰 Статистика зарплат:\n`;
      report += `   Общий фонд: ${salaryStats.total.toLocaleString()}₽\n`;
      report += `   Средняя зарплата: ${Math.round(salaryStats.average).toLocaleString()}₽\n`;
      report += `   Минимальная: ${salaryStats.minimum.toLocaleString()}₽\n`;
      report += `   Максимальная: ${salaryStats.maximum.toLocaleString()}₽\n\n`;
    }

    report += `🏢 Отделы:\n`;
    deptStats.forEach(dept => {
      report += `   ${dept.name}: ${dept.employeeCount} сотр., бюджет ${dept.budget.toLocaleString()}₽\n`;
    });

    return report;
  }
}

// Использование системы
const hrSystem = new EmployeeManagementSystem();

// Создаем отделы
await hrSystem.addDepartment({
  id: 'eng',
  name: 'Engineering',
  budget: 2000000,
  managerId: 'emp_001'
});

await hrSystem.addDepartment({
  id: 'sales',
  name: 'Sales',
  budget: 1500000
});

// Добавляем сотрудников
await hrSystem.addEmployee({
  id: 'person_001',
  employeeId: 'emp_001',
  firstName: 'John',
  lastName: 'Smith',
  email: 'john.smith@company.com',
  department: 'Engineering',
  position: 'Senior Developer',
  salary: 120000,
  hireDate: '2023-01-15'
});

await hrSystem.addEmployee({
  id: 'person_002',
  employeeId: 'emp_002',
  firstName: 'Jane',
  lastName: 'Doe',
  email: 'jane.doe@company.com',
  department: 'Sales',
  position: 'Sales Manager',
  salary: 100000,
  hireDate: '2023-03-01'
});

// Операции с сотрудниками
await hrSystem.promoteEmployee('emp_001', 'Tech Lead', 140000);
await hrSystem.transferEmployee('emp_002', 'Engineering');

// Получаем статистику
console.log('\n' + hrSystem.generateReport());

// Валидация
const validation = await hrSystem.validateAllEmployees();
console.log(`\n✅ Валидных сотрудников: ${validation.valid.length}`);
console.log(`❌ Невалидных сотрудников: ${validation.invalid.length}`);
```

## 🎉 Заключение

Типизированные элементы в CSElement обеспечивают:

- **🛡️ Безопасность типов** - автодополнение и проверка во время компиляции
- **✅ Автоматическая валидация** - проверка данных при создании и изменении
- **🏗️ Наследование** - создание иерархий типов
- **🔄 Миграции** - безопасное обновление схем данных
- **📊 Типизированные коллекции** - строго типизированные списки

**Готовы изучить batch операции?** Переходите к [следующему разделу](05-batching.md)! 🚀 