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

Для чего нужен Record?

2.3 Middle🔥 111 комментариев
#Soft Skills и рабочие процессы

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

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

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

Для чего нужен Record?

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

Синтаксис

type Record<K extends string | number | symbol, T> = { [P in K]: T }

Это означает: создать объект, где ключи должны быть типа K, а значения — типа T.

Базовый пример

// Задаём, какие ключи допустимы и какой тип значений
type UserRoles = Record<'admin' | 'user' | 'guest', string>;

// Это эквивалентно:
type UserRoles = {
  admin: string;
  user: string;
  guest: string;
};

// Использование
const roles: UserRoles = {
  admin: 'Administrator',
  user: 'Regular User',
  guest: 'Guest'
};

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

Пример 1: Конфигурация по ролям

type UserRole = 'admin' | 'moderator' | 'user';
type Permissions = 'read' | 'write' | 'delete';

// Определяем, какие разрешения есть у каждой роли
const rolePermissions: Record<UserRole, Permissions[]> = {
  admin: ['read', 'write', 'delete'],
  moderator: ['read', 'write'],
  user: ['read']
};

// Использование
function canDelete(role: UserRole): boolean {
  return rolePermissions[role].includes('delete');
}

console.log(canDelete('admin'));     // true
console.log(canDelete('user'));      // false

Пример 2: Переводы на разные языки

type Language = 'en' | 'ru' | 'de';

// Объект переводов
const translations: Record<Language, Record<string, string>> = {
  en: {
    hello: 'Hello',
    goodbye: 'Goodbye'
  },
  ru: {
    hello: 'Привет',
    goodbye: 'До свидания'
  },
  de: {
    hello: 'Hallo',
    goodbye: 'Auf Wiedersehen'
  }
};

// Использование
function getMessage(lang: Language, key: string): string {
  return translations[lang][key];
}

console.log(getMessage('ru', 'hello')); // 'Привет'

Пример 3: Статус-коды HTTP

type HttpStatus = 200 | 201 | 400 | 401 | 404 | 500;

const httpMessages: Record<HttpStatus, string> = {
  200: 'OK',
  201: 'Created',
  400: 'Bad Request',
  401: 'Unauthorized',
  404: 'Not Found',
  500: 'Internal Server Error'
};

// Использование
function getStatusMessage(code: HttpStatus): string {
  return httpMessages[code];
}

console.log(getStatusMessage(404)); // 'Not Found'

Пример 4: Валидаторы для полей формы

type FieldName = 'email' | 'password' | 'name' | 'age';

type Validator = (value: string) => boolean;

const fieldValidators: Record<FieldName, Validator> = {
  email: (value) => /^[^@]+@[^@]+\.[^@]+$/.test(value),
  password: (value) => value.length >= 8,
  name: (value) => value.length >= 2,
  age: (value) => /^\d+$/.test(value) && parseInt(value) >= 18
};

// Использование
function validateField(fieldName: FieldName, value: string): boolean {
  return fieldValidators[fieldName](value);
}

console.log(validateField('email', 'user@example.com'));  // true
console.log(validateField('password', '123'));             // false
console.log(validateField('age', '25'));                   // true

Record с enum

enum Color {
  Red = 'red',
  Green = 'green',
  Blue = 'blue'
}

type ColorHex = Record<Color, string>;

const colorMap: ColorHex = {
  [Color.Red]: '#FF0000',
  [Color.Green]: '#00FF00',
  [Color.Blue]: '#0000FF'
};

console.log(colorMap[Color.Red]); // '#FF0000'

Record vs интерфейс

// Вариант 1: Record (более краткий)
type UserStats = Record<'likes' | 'comments' | 'shares', number>;

// Вариант 2: Interface (более детальный)
interface UserStatsInterface {
  likes: number;
  comments: number;
  shares: number;
}

// Record лучше, когда ключи динамичные или из типа/enum
type StatusCode = 200 | 404 | 500;
type StatusMessages = Record<StatusCode, string>;

// Interface лучше для больших объектов с методами
interface ApiClient {
  get(url: string): Promise<any>;
  post(url: string, data: any): Promise<any>;
}

Record с readonly

type ReadonlyConfig = Readonly<Record<'apiUrl' | 'timeout' | 'retries', string | number>>;

const config: ReadonlyConfig = {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retries: 3
};

// config.timeout = 10000; // Ошибка: Cannot assign to readonly property

Record с Partial (опциональные значения)

type Settings = Partial<Record<'theme' | 'fontSize' | 'notifications', string>>;

const userSettings: Settings = {
  theme: 'dark'
  // fontSize и notifications необязательны
};

Практический пример из React

import { useState } from 'react';

type TabName = 'profile' | 'settings' | 'notifications';

interface TabContent {
  title: string;
  icon: string;
}

const tabConfig: Record<TabName, TabContent> = {
  profile: {
    title: 'Профиль',
    icon: 'user'
  },
  settings: {
    title: 'Настройки',
    icon: 'gear'
  },
  notifications: {
    title: 'Уведомления',
    icon: 'bell'
  }
};

function TabPanel() {
  const [activeTab, setActiveTab] = useState<TabName>('profile');

  return (
    <div>
      {Object.entries(tabConfig).map(([tab, { title, icon }]) => (
        <button
          key={tab}
          onClick={() => setActiveTab(tab as TabName)}
          className={activeTab === tab ? 'active' : ''}
        >
          {icon} {title}
        </button>
      ))}
      <div>
        {tabConfig[activeTab].title} content here
      </div>
    </div>
  );
}

Итог

Record<K, T> — это полезный TypeScript тип для:

  • Создания типизированных объектов-словарей
  • Определения мап между ключами и значениями
  • Конфигураций, где ключи известны заранее
  • Трансформации типов и enum-ов в объекты
  • Обеспечения типобезопасности при работе с динамическими ключами

Rec используется часто в реальных проектах для конфигураций, переводов, валидаторов и других словарь-подобных структур.

Для чего нужен Record? | PrepBro