Typescript vyžaduje parameter, ak je zadaný iný parameter, viackrát
Snažím sa vytvoriť distribuovaný typ unie, kde pri zadaní jedného kľúča sa stávajú ostatné kľúče povinnými.
interface BaseArgs {
title: string
}
interface FuncPagerArgs {
enablePager: true
limit: number
count: number
}
type FuncArgs = (FuncPagerArgs & BaseArgs) | BaseArgs;
function func(args: FuncArgs) {
if ("enablePager" in args) {
pager({ limit: args.limit, count: args.count });
}
}
interface PagerArgs {
limit: number
count: number
}
function pager(args: PagerArgs) {
}
func({
title: "something",
enablePager: true
})
https://www.typescriptlang.org/play/index.html?ssl=1&ssc=1&pln=31&pc=3#code/JYOwLgpgTgZghgYwgAgEJwM4QIJQOYbIDeAUMucmMGADYQBcyGYUoeJAviSaJLIigBiAVxAIACnDzRcBYmQoQQcAEZ1J0qIxbCIC8jWABbaoxDCjK6PuQIA9qLBmLVqJ25gAngAchohLKEALzIABQiYhoy+IQAZGiYODEAlMgAPglYgQDc3DD+VHYgyPlioXAxjBEBKfIUyMAwYQBESqrqUtDNDcUVBKmk9fXenVChRMiGJk7IfRgAdFPUADS2DuCMc-P2jsgcybn1XFw84NDwSMhRUIF1FEsz5pbW9TsbyE+u7iSlCIXFI005UqV1GgQG3BOv3GNiotAYyGaGDsRggYAAFmxmssbG01BBrtooLpOMkgA
Podľa mňa by tento kód nemal prejsť validáciou, pretože volám func a zároveň odovzdávam enablePager, ale neodovzdal som limit ani count, keď sú oba požadované, ak je enablePager pravda. V mojom reálnom príklade som sa snažil použiť tento vzor pre 3 alebo 4 rôzne boolean hodnoty funkcií, z ktorých každá zmení zmluvu tak, že bude vyžadovať viac polí. Bohužiaľ, ani prvú boolean hodnotu funkcie sa mi nepodarilo rozbehnúť, nieto ešte niekoľko.
Odpoveď č. 1
Problém, ktorý tu vidíme, súvisí s tým, že nemáme správny diskriminant medzi prvkami zjednotenia. Máme prekrytie medzi dvoma členmi (FuncPagerArgs & BaseArgs) | BaseArgs, pričom prekrytie je rovnaké ako BaseArgs. Veľmi dôležité je aj správanie typov argumentov funkcie v TS:
Pri porovnávaní typov parametrov funkcie je priradenie úspešné, ak je buď zdrojový parameter priraditeľný k cieľovému parametru, alebo naopak.
Čo to znamená - ak je náš typ priraditeľný k požadovanému typu, alebo ak je požadovaný typ priraditeľný k nášmu typu argumentu, môžete ho odovzdať. Zvážte test:
type ArgType = {
title: "something",
enablePager: true
}
type IsAssignable = ArgType extends FuncArgs ? true : false; // true, can be passed
Takže typ, ktorý odovzdávate, je priraditeľný k požadovanému typu, potom TS umožní jeho odovzdanie. Nežiadajte ma, prečo, toto sú dizajnové rozhodnutia, a môžete to chápať ako kompromis, byť prísnejší by zablokovalo mnoho požadovaných správaní, aspoň tak je uvedené v dokumentácii TS, na ktorú som uviedol odkaz vyššie. Určite je teraz vo vnútri kódu problém, ktorý spôsobí runtime chybu.
Aby sme to opravili, musíme vytvoriť správnu diskriminovanú úniu bez prekrytia.
interface BaseArgs {
title: string
}
interface OnlyTitle extends BaseArgs {
kind: 'TITLE'
}
interface FuncPagerArgs extends BaseArgs {
kind: 'PAGER'
enablePager: true
limit: number
count: number
}
type FuncArgs = OnlyTitle | FuncPagerArgs;
function func(args: FuncArgs) {
if (args.kind === 'PAGER') {
pager({ limit: args.limit, count: args.count });
}
}
interface PagerArgs {
limit: number
count: number
}
function pager(args: PagerArgs) {
}
func({
kind: "TITLE",
enablePager: true
}) // error
func({
kind: "TITLE",
title: 'title'
}) // correct
To, čo som urobil, je diskriminant vo forme vlastnosti kind, ktorá umožní používanie správnych členov bez akéhokoľvek prekrývania. To znamená, že teraz môžete použiť len jeden alebo druhý variant, typ sa nikdy neprekrýva.
Upraviť po komentári
V komentári autor uviedol, že potrebuje spojiť všetky možnosti dohromady, ale keď je niečo povolené, všetky možnosti s tým súvisiace by mali byť zahrnuté. Takýto druh štruktúry môžeme dosiahnuť rozdelením možností do skupín + ich spojením. Zvážte:
interface Base {
title: string
}
interface Pager {
pager?: {
limit: number
count: number
}
}
interface Sort {
sorter?: {
columns: string[] // example property
}
}
type Options = Pager & Sort & Base;
function func(args: Options) {
if (args.pager) {
pager({ limit: args.pager.limit, count: args.pager.count });
}
if (args.sorter) {
sorter(args.sorter.columns)
}
}
func({
title: 'title',
pager: {count: 2, limit: 10} // all fields its ok
})
func({
title: 'title',
pager: {count: 2} // error limit is missing
})
Teraz môžeme mať pager a sorter spolu, keď máte pager, musíte umiestniť všetky možnosti s ním súvisiace, pretože sú potrebné.
