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>.
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:
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:
- 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
Zatím žádné komentáře.