Перейти к содержанию

Языки программирования

545 Темы 741 Сообщения

Синтаксис, библиотеки, фреймворки, алгоритмы, ООП, функциональное, асинхронное, многопоточное программирование. Помощь новичкам, советы экспертов, тренды и кейсы. Решайте задачи, делитесь кодом.


  • 60 79
    60 Темы
    79 Сообщения
    AladdinA
    Декоратор в Python это обычная функция, которая принимает другую функцию (или класс) как аргумент, расширяет её поведение и возвращает новый вызываемый объект. Синтаксис @decorator это просто синтаксический сахар: запись @timer над функцией foo полностью эквивалентна foo = timer(foo). def my_decorator(func): def wrapper(*args, **kwargs): print("до вызова") result = func(*args, **kwargs) print("после вызова") return result return wrapper @my_decorator def greet(name): print(f"Привет, {name}!") # Эквивалентно: greet = my_decorator(greet) greet("Alice") # до вызова # Привет, Alice! # после вызова Механизм работы в три шага: Python видит @decorator, вызывает decorator(func) в момент определения функции (не при вызове), сохраняет результат под тем же именем. Это важно: код декоратора вне wrapper выполняется один раз при загрузке модуля. functools.wraps — первое правило декораторов Без @wraps задекорированная функция теряет своё имя, docstring и сигнатуру. Это ломает отладку, help(), inspect, Sphinx-документацию и любые инструменты интроспекции. from functools import wraps def my_decorator(func): @wraps(func) # копирует __name__, __doc__, __module__, __annotations__ def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @my_decorator def add(a: int, b: int) -> int: """Складывает два числа.""" return a + b print(add.__name__) # add (без @wraps было бы 'wrapper') print(add.__doc__) # Складывает два числа. @wraps под капотом вызывает functools.update_wrapper, который копирует атрибуты __module__, __name__, __qualname__, __annotations__, __doc__ и обновляет __wrapped__, позволяя при необходимости добраться до оригинальной функции через add.__wrapped__. Встроенные декораторы — стандартная библиотека @property Превращает метод в управляемый атрибут с геттером, сеттером и делитером. Позволяет добавить валидацию без изменения публичного API класса. class Temperature: def __init__(self, celsius: float = 0): self._celsius = celsius @property def celsius(self) -> float: return self._celsius @celsius.setter def celsius(self, value: float): if value < -273.15: raise ValueError("Ниже абсолютного нуля невозможно") self._celsius = value @property def fahrenheit(self) -> float: return self._celsius * 9 / 5 + 32 t = Temperature(100) print(t.fahrenheit) # 212.0 t.celsius = -300 # ValueError @staticmethod и @classmethod @staticmethod объявляет метод, не привязанный ни к экземпляру (self), ни к классу (cls это просто функция внутри пространства имён класса. @classmethod передаёт сам класс первым аргументом, что позволяет создавать альтернативные конструкторы. class Date: def __init__(self, year: int, month: int, day: int): self.year, self.month, self.day = year, month, day @classmethod def from_string(cls, s: str) -> "Date": year, month, day = map(int, s.split("-")) return cls(year, month, day) # работает и в подклассах @staticmethod def is_leap_year(year: int) -> bool: return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) d = Date.from_string("2024-03-15") print(Date.is_leap_year(2024)) # True @dataclass Автоматически генерирует __init__, __repr__, __eq__ и, опционально, методы сравнения и хэширования. Начиная с Python 3.10 можно использовать slots=True для экономии памяти. from dataclasses import dataclass, field @dataclass(order=True, frozen=True) class Point: x: float y: float label: str = field(default="", compare=False) p1 = Point(1.0, 2.0, "A") p2 = Point(3.0, 4.0, "B") print(p1 < p2) # True (сравнивает x, затем y) # p1.x = 5.0 # FrozenInstanceError, т.к. frozen=True Параметры eq, order, frozen, slots, kw_only (3.10+) и match_args (3.10+) позволяют точно настроить поведение. @abstractmethod Из модуля abc. Помечает метод как абстрактный: класс, содержащий хотя бы один такой метод, нельзя инстанциировать. Подкласс обязан переопределить все абстрактные методы. from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self) -> float: ... @abstractmethod def perimeter(self) -> float: ... class Circle(Shape): def __init__(self, r: float): self.r = r def area(self) -> float: return 3.14159 * self.r ** 2 def perimeter(self) -> float: return 2 * 3.14159 * self.r # Shape() # TypeError: Can't instantiate abstract class c = Circle(5) @cached_property Добавлен в Python 3.8. Вычисляет значение при первом обращении, кэширует его в __dict__ экземпляра и больше не вызывает getter. В отличие от @property нет лишнего вызова при каждом обращении. from functools import cached_property import statistics class Dataset: def __init__(self, data: list[float]): self._data = data @cached_property def stats(self) -> dict: print("вычисляем...") # выполнится только один раз return { "mean": statistics.mean(self._data), "stdev": statistics.stdev(self._data), } ds = Dataset([1, 2, 3, 4, 5]) print(ds.stats) # вычисляем... {'mean': 3, 'stdev': 1.58...} print(ds.stats) # из кэша, без "вычисляем..." Важно: не работает с __slots__ и не потокобезопасен без внешней блокировки. @override (Python 3.12+) Из модуля typing. Сигнализирует тайп-чекеру, что метод переопределяет родительский. Если в родителе метод переименовали или удалили, тайп-чекер выдаст ошибку. from typing import override class Base: def process(self, data: str) -> str: return data.upper() class Child(Base): @override def process(self, data: str) -> str: # ошибка, если в Base нет process return data.lower() @deprecated (Python 3.13+) Из модуля warnings. Помечает функцию или класс как устаревший: при вызове автоматически выдаётся DeprecationWarning, а тайп-чекеры отображают предупреждение. from warnings import deprecated @deprecated("Используйте new_api() вместо этого") def old_api(): return 42 old_api() # DeprecationWarning: Используйте new_api() вместо этого functools — арсенал высшего порядка @lru_cache и @cache @lru_cache(maxsize=N) кэширует результаты функции по аргументам (мемоизация), выбрасывая наименее использованные записи при достижении лимита. @cache (Python 3.9+) это @lru_cache(maxsize=None) без ограничения размера. from functools import lru_cache, cache @lru_cache(maxsize=128) def fibonacci(n: int) -> int: if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) print(fibonacci(50)) # мгновенно print(fibonacci.cache_info()) # CacheInfo(hits=48, misses=51, maxsize=128, currsize=51) fibonacci.cache_clear() # сбросить кэш # Для чистых функций без ограничения размера: @cache def factorial(n: int) -> int: return n * factorial(n - 1) if n else 1 Аргументы должны быть хэшируемыми. Список, словарь или другой изменяемый тип вызовет TypeError. Для методов экземпляра лучше использовать @cached_property или явно кэшировать через словарь. @singledispatch Реализует перегрузку функций по типу первого аргумента (single dispatch, аналог pattern matching по типу). from functools import singledispatch @singledispatch def process(value): raise TypeError(f"Неподдерживаемый тип: {type(value)}") @process.register(int) def _(value: int) -> str: return f"Целое: {value * 2}" @process.register(str) def _(value: str) -> str: return f"Строка: {value.upper()}" @process.register(list) @process.register(tuple) def _(value) -> str: return f"Последовательность длиной {len(value)}" print(process(5)) # Целое: 10 print(process("hello")) # Строка: HELLO print(process([1, 2, 3])) # Последовательность длиной 3 Для методов классов используется functools.singledispatchmethod (Python 3.8+). @total_ordering Если определить __eq__ и один из методов сравнения (__lt__, __le__, __gt__, __ge__), @total_ordering автоматически выведет остальные три. from functools import total_ordering @total_ordering class Version: def __init__(self, major: int, minor: int, patch: int): self.v = (major, minor, patch) def __eq__(self, other) -> bool: return self.v == other.v def __lt__(self, other) -> bool: return self.v < other.v v1 = Version(1, 2, 3) v2 = Version(2, 0, 0) print(v1 < v2) # True print(v1 >= v2) # False (сгенерировано @total_ordering) print(v2 > v1) # True (сгенерировано @total_ordering) Декораторы с параметрами Чтобы передать аргументы в декоратор, нужен ещё один уровень вложенности: функция, возвращающая декоратор. from functools import wraps import time def retry(max_attempts: int = 3, delay: float = 1.0, exceptions=(Exception,)): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): last_exc = None for attempt in range(1, max_attempts + 1): try: return func(*args, **kwargs) except exceptions as e: last_exc = e print(f"Попытка {attempt}/{max_attempts} не удалась: {e}") if attempt < max_attempts: time.sleep(delay) raise last_exc return wrapper return decorator @retry(max_attempts=3, delay=0.5, exceptions=(ConnectionError,)) def fetch_data(url: str) -> str: # симулируем нестабильное соединение import random if random.random() < 0.7: raise ConnectionError("Нет соединения") return "data" Начиная с Python 3.8 можно сделать декоратор, работающий как с аргументами (@retry(3)), так и без (@retry), через параметр func=None и позиционно-ключевые аргументы: def retry(_func=None, *, max_attempts: int = 3): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): for _ in range(max_attempts): try: return func(*args, **kwargs) except Exception: pass raise RuntimeError("Все попытки исчерпаны") return wrapper if _func is not None: # вызов без скобок: @retry return decorator(_func) return decorator # вызов со скобками: @retry(max_attempts=5) Стекирование декораторов Несколько декораторов применяются снизу вверх: ближайший к функции оборачивает первым. from functools import wraps def bold(func): @wraps(func) def wrapper(*args, **kwargs): return f"<b>{func(*args, **kwargs)}</b>" return wrapper def italic(func): @wraps(func) def wrapper(*args, **kwargs): return f"<i>{func(*args, **kwargs)}</i>" return wrapper @bold @italic def greet(name: str) -> str: return f"Привет, {name}!" # Эквивалентно: greet = bold(italic(greet)) print(greet("Alice")) # <b><i>Привет, Alice!</i></b> Порядок имеет значение. @bold @italic дают <b><i>...</i></b>, тогда как @italic @bold дадут <i><b>...</b></i>. Декораторы классов Декоратор может быть не только функцией, но и классом. В таком случае __call__ становится телом обёртки, а __init__ получает декорируемую функцию. from functools import wraps, update_wrapper import time class Timer: """Декоратор-класс: замеряет время выполнения.""" def __init__(self, func): self.func = func self.total_time = 0.0 self.call_count = 0 update_wrapper(self, func) # аналог @wraps для классов def __call__(self, *args, **kwargs): start = time.perf_counter() result = self.func(*args, **kwargs) elapsed = time.perf_counter() - start self.total_time += elapsed self.call_count += 1 print(f"{self.func.__name__}: {elapsed:.4f}с") return result def stats(self): avg = self.total_time / self.call_count if self.call_count else 0 return {"total": self.total_time, "calls": self.call_count, "avg": avg} @Timer def slow_sum(n: int) -> int: return sum(range(n)) slow_sum(1_000_000) slow_sum(500_000) print(slow_sum.stats()) Декоратор-класс удобен, когда нужно хранить состояние между вызовами (счётчик, накопленное время, кэш). Применение декоратора к классу целиком Декоратор можно применить и к классу: Python передаёт объект класса, а не функции. def add_repr(cls): """Добавляет __repr__, если его нет.""" if "__repr__" not in cls.__dict__: def __repr__(self): attrs = ", ".join( f"{k}={v!r}" for k, v in self.__dict__.items() ) return f"{cls.__name__}({attrs})" cls.__repr__ = __repr__ return cls @add_repr class Config: def __init__(self, host: str, port: int): self.host = host self.port = port print(Config("localhost", 8080)) # Config(host='localhost', port=8080) Практические паттерны Логирование import logging from functools import wraps def log_calls(logger=None, level=logging.DEBUG): _logger = logger or logging.getLogger(__name__) def decorator(func): @wraps(func) def wrapper(*args, **kwargs): _logger.log(level, "Вызов %s args=%s kwargs=%s", func.__name__, args, kwargs) try: result = func(*args, **kwargs) _logger.log(level, "%s вернул %r", func.__name__, result) return result except Exception as exc: _logger.exception("%s выбросил %s", func.__name__, exc) raise return wrapper return decorator logging.basicConfig(level=logging.DEBUG) @log_calls() def divide(a: float, b: float) -> float: return a / b Rate Limiter import time from collections import deque from functools import wraps def rate_limit(calls: int, period: float): """Не более `calls` вызовов за `period` секунд.""" def decorator(func): timestamps: deque = deque() @wraps(func) def wrapper(*args, **kwargs): now = time.monotonic() while timestamps and now - timestamps >= period: timestamps.popleft() if len(timestamps) >= calls: sleep_for = period - (now - timestamps) time.sleep(sleep_for) timestamps.append(time.monotonic()) return func(*args, **kwargs) return wrapper return decorator @rate_limit(calls=5, period=1.0) def call_api(endpoint: str) -> dict: return {"endpoint": endpoint, "ok": True} Singleton from functools import wraps import threading def singleton(cls): instances = {} lock = threading.Lock() @wraps(cls, updated=[]) def get_instance(*args, **kwargs): if cls not in instances: with lock: if cls not in instances: # double-checked locking instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance @singleton class DatabasePool: def __init__(self, dsn: str = "sqlite:///:memory:"): self.dsn = dsn print(f"Создан пул: {dsn}") a = DatabasePool("postgresql://localhost/db") b = DatabasePool() # "Создан пул" не печатается второй раз print(a is b) # True Валидация типов import inspect from functools import wraps def validate_types(func): sig = inspect.signature(func) hints = func.__annotations__ @wraps(func) def wrapper(*args, **kwargs): bound = sig.bind(*args, **kwargs) bound.apply_defaults() for name, value in bound.arguments.items(): if name in hints and name != "return": expected = hints[name] if not isinstance(value, expected): raise TypeError( f"Параметр '{name}': ожидался {expected.__name__}, " f"получен {type(value).__name__}" ) return func(*args, **kwargs) return wrapper @validate_types def power(base: float, exp: int) -> float: return base ** exp power(2.0, 3) # OK power(2.0, 3.5) # TypeError: Параметр 'exp': ожидался int, получен float Асинхронные декораторы Декоратор не знает заранее, синхронная функция или async. Для универсального декоратора нужно проверять это явно. import asyncio import time import inspect from functools import wraps def timeit(func): @wraps(func) async def async_wrapper(*args, **kwargs): start = time.perf_counter() result = await func(*args, **kwargs) print(f"{func.__name__}: {time.perf_counter() - start:.4f}с (async)") return result @wraps(func) def sync_wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) print(f"{func.__name__}: {time.perf_counter() - start:.4f}с (sync)") return result # Выбираем обёртку один раз при декорировании (не при каждом вызове) return async_wrapper if inspect.iscoroutinefunction(func) else sync_wrapper @timeit def cpu_task(n: int) -> int: return sum(range(n)) @timeit async def io_task(delay: float) -> str: await asyncio.sleep(delay) return "done" cpu_task(10_000_000) asyncio.run(io_task(0.1)) Проверка inspect.iscoroutinefunction выполняется один раз при декорировании, а не при каждом вызове, что исключает лишние расходы в рантайме. Декораторы в контексте Python 3.13 и 3.14 В Python 3.13 появился @typing.deprecated для явной пометки устаревшего кода прямо в системе типов. В Python 3.14 аннотации стали отложенными (PEP 649): они не вычисляются при определении функции. Это означает, что декораторы, читающие func.__annotations__ напрямую (например, validate_types выше), теперь получат объект AnnotateFunc, а не уже вычисленный словарь. Для надёжной работы с аннотациями используйте inspect.get_annotations(func, eval_str=True): import inspect def validate_types(func): sig = inspect.signature(func) # eval_str=True вычисляет строковые аннотации и отложенные (3.14+) hints = inspect.get_annotations(func, eval_str=True) ... Шпаргалка — когда что использовать Задача Декоратор Управляемый атрибут @property Метод без self/cls @staticmethod Альтернативный конструктор @classmethod Автогенерация boilerplate @dataclass Абстрактный интерфейс @abstractmethod Ленивое кэширование атрибута @cached_property Мемоизация чистых функций @lru_cache / @cache Перегрузка по типу @singledispatch Вывод методов сравнения @total_ordering Пометить как устаревший @deprecated (3.13+) Явное переопределение метода @override (3.12+) Сохранить метаданные функции @wraps Полезные источники Официальная документация functools — docs.python.org/3/library/functools.html “What’s New In Python 3.13” — docs.python.org/3/whatsnew/3.13.html (про @deprecated, @override) “What’s New In Python 3.14” — docs.python.org/3/whatsnew/3.14.html (про отложенные аннотации PEP 649) Real Python — Primer on Python Decorators — realpython.com/primer-on-python-decorators/ freeCodeCamp — The Python Decorator Handbook — freecodecamp.org/news/the-python-decorator-handbook/ asyncmove.com — The Comprehensive Guide to Python Decorators (2026) — asyncmove.com/blog/2026/01/the-comprehensive-guide-to-python-decorators/ PEP 318 — декораторы функций и методов PEP 614 — расслабление грамматических ограничений на декораторы (3.9+) PEP 649 — отложенные аннотации (3.14) References Fancy Decorators - In this tutorial, you’ll look at what Python decorators are and how you define and use them. Decorat… The Python Decorator Handbook - freeCodeCamp - Python decorators provide an easy yet powerful syntax for modifying and extending the behavior of fu… functools — Higher-order functions and operations on callable … - The functools module is for higher-order functions: functions that act on or return other functions… Python Standard Library | collections | itertools | functools - Dev Genius - Python’s functools module, part of the standard library, provides higher-order functions and operati… Handy Python Decorators. Implementing and Using Advanced… - The Python Standard Library contains four incredibly useful decorators: staticmethod , classmethod ,… What’s New In Python 3.13 — Python 3.14.4 documentation - The biggest changes include a new interactive interpreter, experimental support for running in a fre… What’s New In Python 3.13 — Python 3.14.0rc3 documentation - Editors, Adam Turner and Thomas Wouters,. This article explains the new features in Python 3.13, com… Mastering Python Decorators for Code Reusability and Optimization - Discover how Python decorators can help you write reusable, efficient, and maintainable code by modi… The Comprehensive Guide to Python Decorators - Decorators are one of Python’s most powerful metaprogramming tools, widely used in the industry to w… What’s new in Python 3.14 — Python 3.14.4 documentation - This article explains the new features in Python 3.14, compared to 3.13. … no_type_check_decorator…
  • 380 466
    380 Темы
    466 Сообщения
    hannadevH
    Представь, что ты только начинаешь с JavaScript и хочешь понять, как работают игры. Эта статья покажет, как собрать классическую Змейку на чистом JS с Canvas. Ты научишься рисовать, управлять объектами и запускать цикл - без лишних библиотек. Такой проект поможет разобраться в основах: переменные, функции, события клавиш и отрисовка. За пару часов у тебя будет рабочая игра. Проблемы вроде ‘не двигается змейка’ или ‘сталкивается не там’ решатся сами, когда поймешь логику. Готовим основу: HTML и CSS Сначала создаем файл index.html - это каркас игры. Нам нужен тег canvas, как холст для рисования. Добавим счетчик очков и простые стили, чтобы поле выглядело аккуратно. Без этого JS нечем будет рисовать. Canvas - это элемент, где JS рисует пиксели. Укажи ширину и высоту, например 400x400 пикселей. CSS сделает фон черным, рамку и центрирует все на странице. Это базовый шаг, но без него ничего не запустится. Вот минимальный HTML: <!DOCTYPE html> <html> <head> <title>Змейка</title> <style> body { background: #000; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; font-family: Arial; } canvas { border: 2px solid #fff; background: #111; } #score { color: #fff; font-size: 24px; position: absolute; top: 20px; } </style> </head> <body> <div id="score">Счет: 0</div> <canvas id="game" width="400" height="400"></canvas> <script src="script.js"></script> </body> </html> Canvas - холст размером 400x400. JS будет чистить и рисовать на нем каждый кадр. Стили - черный фон, белая рамка. Flex центрирует игру. Счет - div сверху, обновляем текстом из JS. Инициализируем игру в JavaScript Теперь создай script.js. Первое - достаем canvas и контекст 2d. Это как кисть для рисования линий, кругов, квадратов. Определи размер клетки - скажем 20 пикселей, чтобы поле делилось на сетку. Змейка - массив объектов с координатами x и y. Еда - случайная точка на поле. Переменные для направления, скорости и счета. Начни с пустой змейки в центре, добавь стартовую еду. Пример кода инициализации: const canvas = document.getElementById('game'); const ctx = canvas.getContext('2d'); const grid = 20; const scoreElement = document.getElementById('score'); let snake = [{x: 200, y: 200}]; let dx = grid; let dy = 0; let food = {x: 0, y: 0}; let score = 0; function randomFood() { food.x = Math.floor(Math.random() * (canvas.width / grid)) * grid; food.y = Math.floor(Math.random() * (canvas.height / grid)) * grid; } randomFood(); ctx - контекст для drawRect, fillRect и clearRect. snake - массив сегментов, первый - голова. dx, dy - скорость: grid вправо, 0 вниз. randomFood() - ставит еду на случайную клетку. Управление и движение змейки Добавь слушатель клавиш. Стрелки меняют dx и dy, но не позволяй поворот на 180 градусов - иначе змейка врежется в себя. Функция update двигает голову и добавляет новый сегмент. Если голова на еде - счет++, новая еда, змейка растет. Иначе убирай хвост. Проверяй столкновения со стенами и собой. Это сердце логики игры. Код для клавиш и update: document.addEventListener('keydown', (e) => { if (e.key === 'ArrowLeft' && dx === 0) { dx = -grid; dy = 0; } if (e.key === 'ArrowUp' && dy === 0) { dx = 0; dy = -grid; } if (e.key === 'ArrowRight' && dx === 0) { dx = grid; dy = 0; } if (e.key === 'ArrowDown' && dy === 0) { dx = 0; dy = grid; } }); function update() { const head = {x: snake.x + dx, y: snake.y + dy}; snake.unshift(head); if (head.x === food.x && head.y === food.y) { score++; scoreElement.textContent = `Счет: ${score}`; randomFood(); } else { snake.pop(); } // Столкновения (упрощено) if (head.x < 0 || head.x >= canvas.width || head.y < 0 || head.y >= canvas.height) { location.reload(); // Перезапуск } for (let i = 1; i < snake.length; i++) { if (head.x === snake[i].x && head.y === snake[i].y) { location.reload(); } } } keydown - меняет направление, проверка на разворот. unshift/pop - добавляет голову, убирает хвост. Столкновения - с краем или телом = reload. Отрисовка и игровой цикл Функция draw чистит canvas и рисует все. Змейка - зеленые квадраты, еда - красная. Используй fillStyle и fillRect. Главный цикл с requestAnimationFrame обновляет и рисует 10 раз в секунду. Скорость регулируй счетчиком кадров. Это создает плавность без лагов. Запусти цикл сразу после инициализации. Полный цикл: let count = 0; function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = 'red'; ctx.fillRect(food.x, food.y, grid - 2, grid - 2); ctx.fillStyle = 'lime'; snake.forEach((part, i) => { ctx.fillRect(part.x, part.y, grid - 2, grid - 2); }); } function loop() { count++; if (count > 10) { update(); count = 0; } draw(); requestAnimationFrame(loop); } loop(); Элемент Цвет Функция Еда red fillRect с отступом Змейка lime Цикл по сегментам Фон #111 clearRect каждый раз clearRect - стирает кадр. requestAnimationFrame - ~60 FPS, но update реже. count - замедляет движение. Что добавит твоей Змейке огонька Собрал базовую версию? Теперь подумай о паузе на Space, уровнях скорости или лучшей графике. Можно хранить рекорд в localStorage. Это расширит проект без фреймворков. Логика простая, но в ней все основы JS: циклы, объекты, события. Поэкспериментируй с размером grid или формами еды. Дальше - тетрис или платформер на тех же принципах.
  • 58 83
    58 Темы
    83 Сообщения
    kirilljsxK
    Давайте разбираться Record<Keys, Type> - это встроенный utility type в TypeScript, который создает объектный тип-словарь с фиксированным типом ключей и фиксированным типом значений. Для не понимающих: Record<ТипКлюча, ТипЗначения> Объекты все помнят? Ключ -> значение. Под капотом конечно страшненький отображаемый тип: type Record<K extends keyof any, T> = { [P in K]: T; }; Ладно, теперь давай разберем далее несколько примеров, с простой типизацией и с типизацией более боевой что используется в реальных проектах. Базовое использование: const scores: Record<string, number> = { alex: 3, max: 8, kate: 5, }; Обратим внимание на сие простой объект с использованием Record<> теперь картина стала понятнее? У нас есть объект как всегда ключ -> значение, а Record сразу добавляет к ним типизацию. Более приемлемый вариант Представим что у нас есть где-то типы, пусть будут как всегда User, то тогда наш объект должен выглядеть так: type User = { name: string; age: number; } export type { User } Как обычно типы экспортируем Далее их импортируем, и вот смотрите как красиво получается типизировать наш объект через Record import type { User } from "@/types" const scores: Record<string, User> = { user1: { name: 'Alex', age: 30 }, user2: { name: 'Max', age: 25 }, user3: { name: 'Kate', age: 28 }, } А без Record можно? А то, конечно можно, но вопрос надо ли оно нам? Вот пару способов: // 1. Индексная сигнатура - ну классика const scores: { [key: string]: number } = { alex: 3, max: 8, kate: 5, }; // 2. Через interface interface Scores { [key: string]: number; } const scores: Scores = { alex: 3, max: 8, kate: 5, }; Думаю у многих будет один и тот же вопрос, а моги я сделать так: interface Scores { name: number; } const scores: Scores = { name: 42, // Если тут будет name, а не alex - то сработает max: 3, kate: 8, }; Нет, не можете - потому что name: number описывает конкретное поле с именем name. TypeScript не разрешит добавить max или kate, так как их нет в интерфейсе. Для динамических ключей нужна индексная сигнатура [key: string]. По сути Record - это короткая запись для индексной сигнатуры. Квадратные скобки [key: string] говорят TS: “Эй мужик, ключи могут быть любыми строками”, а тип после двоеточия задает тип значений. Record<string, number> - это то же самое что { [key: string]: number }, только читается лучше и легче. Главное разобраться в самом начале - потом уже будете использовать по наитию.