0Pricing
TypeScript Academy · Урок

Построители со строгой типизацией

Проектируйте цепочки построителей, накапливающие типизированные параметры; обеспечивайте обязательные шаги и допустимые сочетания с помощью обобщений и условных типов.

«Построители со строгой типизацией» — бесплатный урок 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 — локальная установка не требуется.

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

  1. Rest-элементы кортежей и типы каррирования
  2. Безопасная композиция функций и аргументов
  3. Построители со строгой типизацией
← Назад к TypeScript Academy