Плавные интерфейсы с типами
Выстраивайте цепочки методов, отслеживая накопленное состояние в типах
«Плавные интерфейсы с типами» — бесплатный урок TypeScript Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения TypeScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс TypeScript Academy содержит 4 уроков всего.
Отслеживание заданных значений
Обычный сборщик позволяет вызвать build() в любой момент, даже если обязательные поля не заданы. Можно сделать лучше: отслеживать накопленное состояние в системе типов с помощью параметра обобщённого типа.
Параметр типа состояния
Мы задаём сборщику обобщённый тип S, который записывает, какие ключи уже были переданы. При каждом вызове метода мы расширяем S, добавляя новый ключ.
class Builder<S> {
// S is a record of keys set so far, e.g. {} or { url: string }
private data: Record<string, unknown> = {};
}Каждый метод добавляет ключ
Метод установки возвращает новый типизированный сборщик, чей S включает только что добавленный ключ. Во время выполнения значение сохраняется, а тип соответственно расширяется.
set<K extends string, V>(key: K, value: V): Builder<S & Record<K, V>> {
this.data[key] = value;
return this as unknown as Builder<S & Record<K, V>>;
}Приём с пересечением типов
Выражение S & Record<K, V> пересекает предыдущее состояние с новым ключом. После двух вызовов тип точно знает об обоих ключах.
type Empty = {};
type AfterUrl = Empty & Record<"url", string>;
type AfterBoth = AfterUrl & Record<"method", string>;
// AfterBoth = { url: string; method: string }Конкретный сборщик с цепочкой вызовов
Здесь сборщик запроса переносит накопленную структуру через каждый вызов. Компилятор всегда точно знает, какие поля существуют.
class Req<S> {
private d: Record<string, unknown> = {};
url(u: string): Req<S & { url: string }> {
this.d.url = u; return this as any;
}
method(m: string): Req<S & { method: string }> {
this.d.method = m; return this as any;
}
}Тип расширяется по мере цепочки вызовов
Каждый вызов в цепочке создаёт более конкретный тип сборщика. Если навести указатель на результат в редакторе, можно увидеть накопленное состояние.
const partial = new Req<{}>().url("/x");
// type: Req<{ url: string }>
const full = partial.method("POST");
// type: Req<{ url: string; method: string }>Зачем вообще отслеживать состояние?
Когда тип содержит множество заданных ключей, мы можем позднее ограничить build(), разрешив компиляцию только при наличии обязательных ключей. Параметр состояния служит основой этой гарантии.
Чтение накопленной структуры
Вы можете извлечь накопленную структуру и изучить её. Это подтверждает, что тип сборщика точно отражает всё настроенное состояние.
type StateOf<T> = T extends Req<infer S> ? S : never;
type S1 = StateOf<Req<{ url: string }>>; // { url: string }Время выполнения остаётся простым
Вся сложность находится в типах. Во время выполнения каждый метод установки просто сохраняет значение в объекте. Приведения типов (as any) связывают объект во время выполнения с более богатым типом на этапе компиляции.
const r = new Req<{}>().url("/users").method("GET");
console.log("configured url and method");Обобщённое состояние как журнал
Представляйте S как журнал заданных значений. Методы добавляют в него записи, а на этапе сборки можно прочитать журнал и решить, разрешено ли создание объекта.
Компромиссы
Отслеживание состояния на уровне типов даёт точные подсказки среды разработки и предотвращает неполную сборку, но требует нескольких приведений типов и более сложных обобщённых типов. Для интерфейсов, которыми пользуются многие, это себя оправдывает.
Быстрая проверка
Быстрая проверка по этому уроку.
Повторение
Типизированный сборщик с цепочкой вызовов содержит обобщённый параметр состояния S. Каждый метод установки пересекает S с новым ключом (S & Record<K, V>) и возвращает более конкретный сборщик, предоставляя компилятору точный журнал настроенных полей.
Часто задаваемые вопросы
Урок «Плавные интерфейсы с типами» бесплатный?
Да — полный текст урока «Плавные интерфейсы с типами» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс TypeScript Academy, подпишись на CoddyKit PRO. Курс TypeScript Academy содержит 4 уроков всего.
Чему я научусь в уроке «Плавные интерфейсы с типами»?
Выстраивайте цепочки методов, отслеживая накопленное состояние в типах Ты практикуешь TypeScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать TypeScript Academy?
Предыдущий опыт не требуется. TypeScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Плавные интерфейсы с типами»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке TypeScript Academy?
Да. Каждый урок TypeScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Основы шаблона Builder
- Плавные интерфейсы с типами
- Обязательные этапы
- Неизменяемые Builder-объекты