Построители со строгой типизацией
Проектируйте цепочки построителей, накапливающие типизированные параметры; обеспечивайте обязательные шаги и допустимые сочетания с помощью обобщений и условных типов.
«Построители со строгой типизацией» — бесплатный урок TypeScript Academy на CoddyKit. Это урок 3 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения TypeScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс TypeScript Academy содержит 3 уроков всего.
Введение
Цель: создавать цепочечные APIs, в которых прогресс кодируется в типе. Каждый шаг возвращает новое типизированное состояние, поэтому недопустимый порядок вызовов обнаруживается при компиляции.
- Накапливать параметры в обобщённом типе
- Ограничивать шаги с помощью флагов
- Сохранять удобство разработки DX
Флаги состояния
Каждый шаг возвращает новый тип состояния; build доступен только тогда, когда необходимые флаги имеют значение true.
type Flags = { method: boolean; url: boolean }
type With<F extends Partial<Flags>> = { flags: F }
type True = true
type Builder<F extends Partial<Flags>, O extends Record<string, unknown>> = With<F> & {
method<M extends "GET" | "POST">(m: M): Builder<F & { method: True }, O & { method: M }>
url<U extends `/${string}`>(u: U): Builder<F & { url: True }, O & { url: U }>
build(this: Builder<{ method: True; url: True }, any>): O
}
function createBuilder(): Builder<{}, {}> {
return {
flags: {},
method(m) { return { ...this, flags: { ...this.flags, method: true }, method: this.method, url: this.url, build: this.build } as any },
url(u) { return { ...this, flags: { ...this.flags, url: true }, method: this.method, url: this.url, build: this.build } as any },
build() { return this as any }
} as any
}
const b = createBuilder()
const good = b.method("GET").url("/users").build() // ok
// const bad = b.build() // error: build requires method & url flagsНакопление параметров
Накапливайте параметры в обобщённом состоянии; каждый вызов возвращает тип, добавляющий новые свойства.
type B2<F extends Partial<Flags>, O extends Record<string, unknown>> = With<F> & {
header<K extends string, V extends string>(k: K, v: V): B2<F, O & { headers: Record<K, V> }>
query<K extends string, V extends string>(k: K, v: V): B2<F, O & { query: Record<K, V> }>
method<M extends "GET" | "POST">(m: M): B2<F & { method: True }, O & { method: M }>
url<U extends `/${string}`>(u: U): B2<F & { url: True }, O & { url: U }>
build(this: B2<{ method: True; url: True }, any>): O
}
function builder2(): B2<{}, {}> { return {} as any }
const conf = builder2()
.method("POST")
.url("/login")
.header("x-id", "42")
.query("next", "home")
.build()Допустимые сочетания
Ограничивайте сочетания условными типами: делайте body доступным только для POST.
type Method = "GET" | "POST"
type BodyIfPost<M extends Method, B> = M extends "POST" ? { body: B } : {}
type B3<F extends Partial<Flags>, O extends Record<string, unknown>> = With<F> & {
method<M extends Method>(m: M): B3<F & { method: True }, O & { method: M }>
url<U extends `/${string}`>(u: U): B3<F & { url: True }, O & { url: U }>
body<B>(b: B & (O extends { method: "POST" } ? unknown : never)): B3<F, O & BodyIfPost<O extends { method: infer M } ? Extract<M, Method> : Method, B>>
build(this: B3<{ method: True; url: True }, any>): O
}
function builder3(): B3<{}, {}> { return {} as any }
const ok = builder3().method("POST").url("/u").body({ a: 1 }).build()
// const badBody = builder3().method("GET").url("/u").body({ a: 1 }).build() // compile-time errorКортежный поток
Собирайте шаги в коллекцию с помощью массива кортежей переменной длины; управляйте входными и выходными данными через Parameters/ReturnType.
type Step<A extends any[], R> = (...args: A) => R
type Pipeline<P extends Step<any, any>[]> = {
use<S extends Step<any, any>>(...s: [S]): Pipeline<[...P, S]>
run<A extends Parameters<P[0]>>(this: Pipeline<P>, ...args: A): ReturnType<P[number]>
}
function pipeMake(): Pipeline<[]> { return {} as any }
const pipe = pipeMake()
.use((a: string) => a.length)
.use((n: number) => n % 2 === 0)
// pipe.run("abc") // demo; implementation omittedСоветы
Советы:
Храните состояние в фиктивном обобщённом параметре; не нагружайте им время выполнения.
Делайте имена шагов короткими и фиксированными (метод, url).
Разделяйте сложные сочетания с помощью условных типов; сохраняйте цепочку чистой.
Проверка ограничений построителя
Быстрая проверка: как обеспечить вызов метода перед build()?
Итоги
Итоги: переносите типизированное состояние по цепочке, накапливайте параметры в обобщённых типах и обнаруживайте недопустимые последовательности при компиляции.
Часто задаваемые вопросы
Урок «Построители со строгой типизацией» бесплатный?
Да — полный текст урока «Построители со строгой типизацией» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс TypeScript Academy, подпишись на CoddyKit PRO. Курс TypeScript Academy содержит 3 уроков всего.
Чему я научусь в уроке «Построители со строгой типизацией»?
Проектируйте цепочки построителей, накапливающие типизированные параметры; обеспечивайте обязательные шаги и допустимые сочетания с помощью обобщений и условных типов. Ты практикуешь TypeScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать TypeScript Academy?
Предыдущий опыт не требуется. TypeScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 3.
Сколько времени занимает урок «Построители со строгой типизацией»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке TypeScript Academy?
Да. Каждый урок TypeScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Rest-элементы кортежей и типы каррирования
- Безопасная композиция функций и аргументов
- Построители со строгой типизацией