Zpět na domů

HKT v TypeScriptu: emulace a praxe

Článek rozebírá emulaci Higher-Kinded Types v TypeScriptu prostřednictvím Kind-mappování a knihoven. Jsou popsána omezení arytivity, implementace map pro kontejnery a překážky nativní podpory. Užitečné pro tvorbu type-safe FP-abstrakcí.

Emulace HKT v TypeScriptu: Kind a knihovny
Advertisement 728x90

Emulace higher-kinded typů v TypeScriptu: praktické přístupy

TypeScript nepodporuje typy vyššího řádu (Higher-Kinded Types, HKT) nativně, což omezuje tvorbu univerzálních abstrakcí nad typovými konstruktory jako Array, Set nebo Promise. HKT umožňují parametrizovat nejen typy prvků, ale i samotné kontejnery, zajišťují přísnou typovou bezpečnost pro funkcionální vzory – funktory a monády. Bez nich je nutné duplikovat kód pro mapovací operace u každého datového typu.

Zvažme typický příklad: implementaci map pro Set.

type MapSet = <A, B>(f: (a: A) => B, set: Set<A>) => Set<B>;

Podobné verze budou potřeba pro ReadonlySet, LinkedList nebo ReadonlyArray. Duplikace je nevyhnutelná bez HKT, kde by rozhraní mohlo vypadat jako Mappable<F> s F<A> → F<B>.

Google AdInline article slot

TypeScript vyhazuje chybu: Type ‘F’ is not generic, protože nerozlišuje typové řády ( pro string a → * pro Array).

Emulace prostřednictvím slovníku konstruktorů

Nejjednodušší řešení je použít slovník, který mapuje řetězcové klíče na typy.

interface TypeConstructors<A> {
  'Array': Array<A>;
  'LinkedList': LinkedList<A>;
  'ReadonlySet': ReadonlySet<A>;
  'Set': Set<A>;
}

type Kind<F extends keyof TypeConstructors<unknown>, A> = TypeConstructors<A>[F];

Kind<'Array', number> se odvodí jako Array<number>. Na jeho základě lze vytvořit univerzální typ:

Google AdInline article slot
type Mappable<F extends keyof TypeConstructors<unknown>> = <A, B>(
  f: (a: A) => B,
  list: Kind<F, A>
) => Kind<F, B>;

Implementace pro konkrétní typy:

const mapArray: Mappable<'Array'> = (f, list) => list.map(f);
const mapSet: Mappable<'Set'> = (f, list) => new Set(Array.from(list, f));

Omezení arity a jejich řešení

Tento přístup funguje pro unární konstruktory (). Pro binární (Map<K, V>) je třeba samostatný slovník:

interface TypeConstructors2<A, B> {
  Map: Map<A, B>;
  Record: Record<A, B>;
}

type Kind2<F extends keyof TypeConstructors2<unknown, unknown>, A, B> = TypeConstructors2<A, B>[F];

To komplikuje podporu různých arností. V produkčních projektech se často používají knihovny:

Google AdInline article slot
  • hkt-toolbelt: podpora až do 4 parametrů, nástroje pro funktory a monády.
  • hkt-core: zaměřeno na výkon typů, minimalistické API.

Tyto řešení poskytují odvozování typů blízké nativnímu HKT bez ručního slovníku.

Klíčové výzvy implementace HKT

I přes požadavky (issue #1213, #55280) chybí HKT v roce 2026. Hlavní bariéry jsou:

  • Složitost kompilátoru: zavedení řádů, rozšíření odvozování typů, adaptace na strukturální typovou kontrolu.
  • Výkon: nárůst času kompilace v velkých FP projektech.
  • Design: vyvážení mezi výrazností a kompatibilitou s JS, vyhnutí se syntaktickému přetížení.

Návrhy zahrnují:

  • Speciální označení F<~> pro konstruktory.
  • Zlepšení podmíněných typů bez nové syntaxe.
  • Pragmatické emulace prostřednictvím existujících mechanismů.

Co je důležité

  • HKT se emulují pomocí Kind-mappingů, což zajišťuje přísnou typovou bezpečnost pro kontejnery.
  • Knihovny hkt-toolbelt/hkt-core usnadňují integraci do projektů střední a vyšší úrovně.
  • Arity vyžaduje samostatné slovníky; unární typy jsou prioritou pro základní scénáře.
  • Budoucí podpora je možná prostřednictvím kompromisních vylepšení, aniž by se zhoršoval výkon.
  • Emulace umožňuje implementovat map, flatMap bez duplikace pro Array/Set/Promise.

— Editorial Team

Advertisement 728x90

Číst dál