0Pricing
TypeScript Academy · Урок

Проблема HKT в TypeScript

Поймите, почему обобщённые конструкторы типов трудно реализовать

«Проблема HKT в TypeScript» — бесплатный урок TypeScript Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения TypeScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс TypeScript Academy содержит 4 уроков всего.

Что такое тип высшего рода

Тип высшего рода (HKT) абстрагируется не от конкретного типа, а от конструктора типов: например, от Array или Promise, которым нужен аргумент, чтобы стать настоящим типом.

Сам по себе Array не является типом. Типом является Array<string>. Array — это функция над типами, а HKT позволяют обобщать такие функции.

type StringArray = Array<string>;
type NumberArray = Array<number>;
// Array by itself is a type constructor, not a type

Коротко о родах

Подобно тому как значения имеют типы, конструкторы типов имеют роды. Обычный тип вроде number имеет род *. Array имеет род * -> *: передайте ему один тип и получите тип в ответ. Обобщение по таким конструкторам и называется «высшим родом».

type ValueLike = number;        // kind *
// Array needs one argument:    kind * -> *
// Map needs two arguments:     kind * -> * -> *

Цель: Functor<F>

В языках с поддержкой HKT можно написать единую абстракцию Functor<F> с одним методом map, который работает с любым контейнером F — массивом, вариантом или обещанием. Один интерфейс, множество контейнеров.

interface Wish_Functor<F> {
  map<A, B>(fa: F<A>, f: (a: A) => B): F<B>;
}
// F<A> here is the wish - and TypeScript rejects it

Проблема

TypeScript не поддерживает применение параметра типа к аргументу. Нельзя написать F<A>, когда F является обобщённым параметром. Компилятор сообщает об ошибке «Тип F не является обобщённым».

// interface Functor<F> {
//   map<A, B>(fa: F<A>, f: (a: A) => B): F<B>;
// }
// Error: Type F is not generic

Почему в TS этого нет

Параметры типов в TypeScript имеют порядок первого уровня: они задают конкретные типы, а не конструкторы типов. Не существует синтаксиса, позволяющего сказать: «F — это нечто, принимающее один аргумент типа». Поэтому F<A> не имеет смысла для компилятора.

// You can pass number, string, User as F...
// but not Array, not Promise (the constructors themselves)

Конкретная проблема

Без HKT приходится дублировать map для каждого контейнера: один вариант для массивов, один для опций, один для промисов. Логика идентична, но система типов не может объединить их.

declare function mapArray<A, B>(fa: A[], f: (a: A) => B): B[];
declare function mapOption<A, B>(fa: A | null, f: (a: A) => B): B | null;
// Same shape, but no way to abstract over the container

Что мы хотим выразить

Нам нужна одна сигнатура: если даны контейнер типа A и функция A -> B, нужно вернуть контейнер типа B, сохранив тип контейнера. Массив остаётся массивом, опция — опцией.

// Wish: map<F, A, B>(fa: F<A>, f: (a: A) => B): F<B>
// where F is preserved across the call

Другие языки

В Haskell и Scala HKT поддерживаются изначально, поэтому классы типов вроде Functor, Applicative и Monad можно выразить непосредственно. В TypeScript нужен обходной путь, приближающийся к той же выразительной силе.

// Haskell: class Functor f where fmap :: (a -> b) -> f a -> f b
// TypeScript: needs an encoding trick (next lessons)

Идея кодирования

Стандартный обходной путь — это дефункционализация: вместо непосредственного применения F мы присваиваем каждому контейнеру строковый тег (URI) и храним реестр, сопоставляющий тег и аргумент с конкретным типом. Применение типа превращается в поиск.

// Instead of F<A>, we write Kind<F, A> where F is a URI string
// and a registry resolves it to the real type

Что вы создадите

В следующих уроках вы шаг за шагом создадите кодирование HKT в стиле fp-ts: реестр URItoKind, поиск Kind и наконец универсальный Functor, чей map работает с любым зарегистрированным контейнером.

// Roadmap:
// 1. Type constructors via URI strings
// 2. Kind<F, A> resolution
// 3. Generic Functor + instances

Почему это стоит знать

Вам редко придётся писать такое кодирование самостоятельно, но библиотеки fp-ts и Effect опираются на него. Понимание этого приёма позволит вам читать их типы, писать универсальные абстракции над контейнерами и лучше понимать возможности и ограничения системы типов.

// Recognizing Kind<F, A> in library code is the payoff

Быстрая проверка

Проверьте, насколько хорошо вы понимаете проблему HKT.

Повторение

Вы познакомились с проблемой типов высшего порядка.

  • HKT абстрагируют конструкторы типов вроде Array, а не конкретные типы.
  • Параметры типов в TypeScript относятся к первому порядку, поэтому F<A> недопустим.
  • Из-за этого нельзя создать один универсальный Functor с одним map.
  • Решение — кодирование с использованием URI-тегов и реестра.

Далее: определение конструкторов типов с помощью строк URI.

Часто задаваемые вопросы

Урок «Проблема HKT в TypeScript» бесплатный?

Да — полный текст урока «Проблема HKT в TypeScript» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс TypeScript Academy, подпишись на CoddyKit PRO. Курс TypeScript Academy содержит 4 уроков всего.

Чему я научусь в уроке «Проблема HKT в TypeScript»?

Поймите, почему обобщённые конструкторы типов трудно реализовать Ты практикуешь TypeScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать TypeScript Academy?

Предыдущий опыт не требуется. TypeScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Проблема HKT в TypeScript»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке TypeScript Academy?

Да. Каждый урок TypeScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Проблема HKT в TypeScript
  2. Определение конструкторов типов
  3. Облегчённый шаблон HKT
  4. Обобщённые функторы и мапперы
← Назад к TypeScript Academy