← Назад к вопросам

Встречал ли описание объекта в TypeScript через утилиту Record

2.0 Middle🔥 102 комментариев
#TypeScript

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

TypeScript Record: Описание объектов

Record — это утилита типов TypeScript для создания объектов с предопределённым набором ключей и типом значений. Это мощный и часто используемый инструмент в реальных проектах.

Базовое использование

Record<Keys, ValueType> принимает два параметра:

  • Keys — допустимые ключи объекта
  • ValueType — тип значений для всех ключей
// Описываем объект с фиксированными ключами
type StatusColors = Record<'success' | 'error' | 'warning' | 'info', string>;

const colors: StatusColors = {
  success: '#22c55e',
  error: '#ef4444',
  warning: '#f59e0b',
  info: '#3b82f6'
  // missing: '#000' // ошибка TypeScript — не хватает ключей
};

Типизация гарантирует:

  1. Ровно эти ключи — не меньше и не больше
  2. Каждый ключ имеет значение нужного типа

Практические примеры

1. Конфигурация по профессиям

type Profession = 'frontend' | 'backend' | 'devops' | 'qa';

type ProfessionConfig = Record<Profession, {
  icon: string;
  description: string;
  difficulty: number;
}>;

const professions: ProfessionConfig = {
  frontend: {
    icon: 'react',
    description: 'React, TypeScript, CSS',
    difficulty: 7
  },
  backend: {
    icon: 'python',
    description: 'Python, FastAPI, PostgreSQL',
    difficulty: 8
  },
  devops: {
    icon: 'docker',
    description: 'Docker, Kubernetes, CI/CD',
    difficulty: 9
  },
  qa: {
    icon: 'pytest',
    description: 'Testing, Automation',
    difficulty: 6
  }
};

// Использование
const frontendConfig = professions.frontend; // есть автодополнение
const allProfessions = Object.keys(professions) as Profession[];

2. Маппирование статусов

type QuestionStatus = 'pending' | 'solved' | 'skipped' | 'failed';

const statusMessages: Record<QuestionStatus, string> = {
  pending: 'Ожидание ответа',
  solved: 'Решено правильно',
  skipped: 'Пропущено',
  failed: 'Ошибка в ответе'
};

const statusStyles: Record<QuestionStatus, string> = {
  pending: 'text-gray-500',
  solved: 'text-green-500',
  skipped: 'text-yellow-500',
  failed: 'text-red-500'
};

// Использование
function getStatusDisplay(status: QuestionStatus) {
  return {
    message: statusMessages[status],
    className: statusStyles[status]
  };
}

3. HTTP методы и их свойства

type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';

const methodProperties: Record<HttpMethod, {
  hasBody: boolean;
  isSafe: boolean;
  isIdempotent: boolean;
}> = {
  GET: { hasBody: false, isSafe: true, isIdempotent: true },
  POST: { hasBody: true, isSafe: false, isIdempotent: false },
  PUT: { hasBody: true, isSafe: false, isIdempotent: true },
  DELETE: { hasBody: false, isSafe: false, isIdempotent: true },
  PATCH: { hasBody: true, isSafe: false, isIdempotent: false }
};

// Компилятор проверит, что все методы описаны

Record vs Interface vs Type Literal

// 1. Через Record
type StatusRecord = Record<'active' | 'inactive', boolean>;
const record: StatusRecord = { active: true, inactive: false };

// 2. Через Interface
interface StatusInterface {
  active: boolean;
  inactive: boolean;
}
const iface: StatusInterface = { active: true, inactive: false };

// 3. Через Type Literal
type StatusLiteral = { active: boolean; inactive: boolean };
const literal: StatusLiteral = { active: true, inactive: false };

Различия:

СпособПлюсыМинусы
RecordЛегко масштабировать список ключей, можно программироватьСложнее читать для больших структур
InterfaceКлассическая типизация, расширяемостьНужно писать каждый ключ
Type LiteralПростотой и прозрачностьюДублирование при много ключей

Динамические ключи из массива

// Если ключи приходят из переменной
const professionList = ['frontend', 'backend', 'devops'] as const;

type ProfessionKey = typeof professionList[number]; // 'frontend' | 'backend' | 'devops'

const configs: Record<ProfessionKey, { level: number }> = {
  frontend: { level: 7 },
  backend: { level: 8 },
  devops: { level: 9 }
};

Record с индексными сигнатурами

// Record для любого строкового ключа
type StringMap = Record<string, number>;

const scores: StringMap = {
  alice: 95,
  bob: 87,
  charlie: 92
};

scores.newUser = 100; // OK

// Record с ограничением на ключи
type NumericKeys = Record<number, string>;

const descriptions: NumericKeys = {
  1: 'First',
  2: 'Second',
  3: 'Third'
};

Вложенные Record

type UserRole = 'admin' | 'user' | 'guest';
type Permission = 'read' | 'write' | 'delete';

type RolePermissions = Record<UserRole, Record<Permission, boolean>>;

const permissions: RolePermissions = {
  admin: {
    read: true,
    write: true,
    delete: true
  },
  user: {
    read: true,
    write: true,
    delete: false
  },
  guest: {
    read: true,
    write: false,
    delete: false
  }
};

// Использование
const canUserDelete = permissions.user.delete; // false

Реальный пример: Форма с валидацией

type FormField = 'email' | 'password' | 'name';

type FormErrors = Record<FormField, string>;
type FormValues = Record<FormField, string>;
type FieldValidators = Record<FormField, (value: string) => boolean>;

const validators: FieldValidators = {
  email: (val) => val.includes('@'),
  password: (val) => val.length >= 8,
  name: (val) => val.length > 0
};

const values: FormValues = {
  email: 'user@example.com',
  password: 'password123',
  name: 'John'
};

const errors: FormErrors = {
  email: '',
  password: '',
  name: ''
};

// Валидируем форму
(Object.keys(validators) as FormField[]).forEach(field => {
  if (!validators[field](values[field])) {
    errors[field] = `Invalid ${field}`;
  }
});

Производство: Кэширование результатов

type CacheKey = 'questions' | 'professions' | 'interview';

type Cache = Record<CacheKey, {
  data: any;
  timestamp: number;
  ttl: number;
}>;

const cache: Cache = {
  questions: { data: [], timestamp: Date.now(), ttl: 3600000 },
  professions: { data: [], timestamp: Date.now(), ttl: 7200000 },
  interview: { data: null, timestamp: 0, ttl: 1800000 }
};

// Функция для проверки валидности кэша
function isCacheValid(key: CacheKey): boolean {
  const entry = cache[key];
  return Date.now() - entry.timestamp < entry.ttl;
}

Когда использовать Record

Используй Record когда:

  • Нужно описать объект с фиксированным набором ключей
  • Все значения имеют одинаковый тип (или объединение типов)
  • Ключи удобно выражаются через union type
  • Нужна масштабируемость — добавляешь новый ключ в union, и TypeScript требует добавить его везде

Используй Interface когда:

  • Ключи разнотипные (разные типы значений)
  • Нужна расширяемость через наследование
  • Объект может быть частично заполнен (optional properties)
// Хорошо для Record
type StatusColors = Record<'success' | 'error' | 'warning', string>;

// Лучше для Interface
interface User {
  id: string;
  name: string;
  email: string;
  age?: number;
}

Вывод

Record — это мощный инструмент TypeScript для создания объектов с предопределённым набором ключей. Он особенно полезен для конфигураций, маппингов и любых структур, где ключи можно выразить как union type. Правильное использование Record улучшает типизацию и предотвращает опечатки.

Встречал ли описание объекта в TypeScript через утилиту Record | PrepBro