Last active
December 23, 2021 22:24
-
-
Save msereniti/eb80913bc0f8036749bda3c1e5d5a5d9 to your computer and use it in GitHub Desktop.
An attempt to create Typescript typing that adds context modifiers to parent functions when nested context consumers appears
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any | |
? A | |
: never; | |
type ArrShift<Arr extends any[]> = Arr extends [skip: any, ...use: infer Use] | |
? Use | |
: Arr; | |
type ViewSetup = { | |
modifiers: { [key: string]: (...args: any[]) => unknown }; | |
}; | |
type CombineTwoSetups<A extends ViewSetup, B extends ViewSetup> = { | |
modifiers: { | |
[modifier in keyof A["modifiers"]]: A["modifiers"][modifier]; | |
} & | |
{ | |
[modifier in keyof B["modifiers"]]: B["modifiers"][modifier]; | |
}; | |
}; | |
type CombineSetups<Setups extends ViewSetup[] = ViewSetup[]> = | |
Setups extends [ViewSetup, ViewSetup, ...ViewSetup[]] ? | |
CombineSetups<[CombineTwoSetups<Setups[0], Setups[1]>, ...ArrShift<ArrShift<Setups>>]> : | |
Setups extends [ViewSetup, ViewSetup] ? | |
CombineTwoSetups<Setups[0], Setups[1]> : | |
Setups extends [ViewSetup] ? | |
Setups[0] : | |
ViewSetup | |
type ViewInstance<Setup extends ViewSetup = ViewSetup> = { | |
[modifier in keyof Setup["modifiers"]]: ( | |
...args: ArgumentTypes<Setup["modifiers"][modifier]> | |
) => ViewInstance<{ modifiers: Setup["modifiers"] }>; | |
// looks like ts looses modifier specific key type and omits all modiefers | |
// ) => ViewInstance<{ modifiers: Omit<Setup["modifiers"], modifier> }>; | |
}; | |
type ViewSetupOfChild<Child extends ViewInstance> = Child extends ViewInstance< | |
infer Setup | |
> | |
? Setup | |
: never; | |
type ViewSetupOfChildren<Children extends ViewInstance[]> = | |
Children extends [ViewInstance, ViewInstance, ...ViewInstance[]] ? | |
CombineTwoSetups<ViewSetupOfChild<Children[0]>, ViewSetupOfChildren<ArrShift<Children>>> : | |
Children extends [ViewInstance, ViewInstance] ? | |
CombineTwoSetups<ViewSetupOfChild<Children[0]>, ViewSetupOfChild<Children[1]>> : | |
Children extends [ViewInstance] ? | |
ViewSetupOfChild<Children[0]> : | |
ViewSetup | |
type TextViewSetup = { | |
modifiers: { | |
fontSize: (fontSize: "title" | "subtitle") => unknown; | |
color: (color: "black" | "blue") => unknown; | |
}; | |
}; | |
type TextView = (content: string) => ViewInstance<TextViewSetup>; | |
type View<OwnSetup extends ViewSetup = ViewSetup> = < | |
Children extends ViewInstance<ViewSetup>[] = ViewInstance<ViewSetup>[], | |
>( | |
...children: Children | |
) => ViewInstance< | |
CombineTwoSetups<ViewSetupOfChildren<Children>, OwnSetup> | |
>; | |
type VStack = View<{ | |
modifiers: { | |
gap: (gap: "small" | "big") => unknown; | |
}; | |
}>; | |
const text: TextView = 0 as any; | |
const view: View<{ modifiers: {} }> = 0 as any; | |
const vStack: VStack = 0 as any; | |
vStack( | |
view(text('Hello world')).color('blue'), | |
text('Hello world') | |
).fontSize('title') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Platform specific declaration | |
const browserRenderer = 'browser_renderer'; | |
type BrowserViewDeclaration< | |
TagName extends keyof HTMLElementTagNameMap = keyof HTMLElementTagNameMap | |
> = { | |
renderer: typeof browserRenderer; | |
params: { | |
tagName: TagName; | |
attributes: Partial<HTMLElementTagNameMap[TagName]>; | |
eventHandlers: TagName extends keyof HTMLElementEventMap | |
? Partial<HTMLElementEventMap[TagName]> | |
: {}; | |
children: ViewInstance[]; | |
}; | |
}; | |
// Declaration | |
type ViewDeclaration = BrowserViewDeclaration; // | OtherPlatformViewDeclaration | |
type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any | |
? A | |
: never; | |
type ArrayLast<Arr extends any[]> = Arr extends [ | |
...skip: unknown[], | |
last: infer Last | |
] | |
? Last | |
: Arr[0]; | |
type ViewSetup = { | |
properties?: any[]; | |
modifiers: { [key: string]: (...args: any[]) => unknown }; | |
}; | |
type ArrayPop<Arr extends any[]> = Arr extends [ | |
...use: infer Use, | |
skip: unknown | |
] | |
? Use | |
: []; | |
type CombineTwoSetups< | |
SecondarySetup extends ViewSetup, | |
PrimarySetup extends ViewSetup | |
> = { | |
properties: PrimarySetup['properties']; | |
modifiers: { | |
[modifier in keyof SecondarySetup['modifiers']]: SecondarySetup['modifiers'][modifier]; | |
} & { | |
[modifier in keyof PrimarySetup['modifiers']]: PrimarySetup['modifiers'][modifier]; | |
}; | |
}; | |
type ViewInstance<Setup extends ViewSetup = ViewSetup> = { | |
[modifier in keyof Setup['modifiers']]: ( | |
...args: ArgumentTypes<Setup['modifiers'][modifier]> | |
) => ViewInstance<{ | |
properties: Setup['properties']; | |
modifiers: Setup['modifiers']; | |
}>; | |
}; | |
type ViewSetupOfChild<Child extends ViewInstance> = Child extends ViewInstance< | |
infer Setup | |
> | |
? Setup | |
: never; | |
type ViewSetupOfChildren<Children extends ViewInstance[]> = Children extends [ | |
ViewInstance, | |
ViewInstance, | |
...ViewInstance[] | |
] | |
? CombineTwoSetups< | |
ViewSetupOfChildren<ArrayPop<Children>>, | |
ViewSetupOfChild<ArrayLast<Children>> | |
> | |
: Children extends [ViewInstance, ViewInstance] | |
? CombineTwoSetups< | |
ViewSetupOfChild<Children[0]>, | |
ViewSetupOfChild<Children[1]> | |
> | |
: Children extends [ViewInstance] | |
? ViewSetupOfChild<Children[0]> | |
: ViewSetup; | |
type View<Setup extends ViewSetup = ViewSetup> = < | |
Children extends ViewInstance<ViewSetup>[] = ViewInstance<ViewSetup>[] | |
>( | |
...properties: Setup['properties'] extends [any, ...any] | |
? Children extends [any, ...any] | |
? [...Setup['properties'], ...Children] | |
: Setup['properties'] | |
: Children | |
) => ViewInstance<CombineTwoSetups<ViewSetupOfChildren<Children>, Setup>>; | |
type ModifiersMap<Setup extends ViewSetup> = { | |
[modifier in keyof Setup['modifiers']]: ArgumentTypes< | |
Setup['modifiers'][modifier] | |
> extends [any, any, ...any] | |
? ArgumentTypes<Setup['modifiers'][modifier]> | |
: ArgumentTypes<Setup['modifiers'][modifier]>[0]; | |
}; | |
type ViewFunc<Setup extends ViewSetup = ViewSetup> = < | |
Children extends ViewInstance<ViewSetup>[] = ViewInstance<ViewSetup>[] | |
>( | |
modifiers: ModifiersMap<Setup>, | |
...properties: Setup['properties'] extends [any, ...any] | |
? Children extends [any, ...any] | |
? [...Setup['properties'], ...Children] | |
: Setup['properties'] | |
: Children | |
) => ViewDeclaration; | |
type CreateView = <Setup extends ViewSetup = ViewSetup>( | |
viewFunc: ViewFunc<Setup> | |
) => View<Setup>; | |
// Fake runtime | |
const createView: CreateView = (() => ({})) as any | |
const vStack = createView<{ | |
modifiers: { | |
gap: (gap: 'small' | 'big') => any; | |
}; | |
}>((...args): any => { | |
console.log(args) | |
// some runtime realization | |
}); | |
const text = createView<{ | |
properties: [content: string]; | |
modifiers: { | |
fontSize: (fontSize: 'title' | 'subtitle') => any; | |
color: (color: 'black' | 'blue') => any; | |
}; | |
}>((...args): any => { | |
console.log(args) | |
// some runtime realization | |
}); | |
// Fun part | |
vStack( | |
vStack(text('Hello world')).color('blue'), | |
text('Hello world') | |
).color('black'). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment