Issue
I'm working on an opensource JS framework and I want to use JSX with typescript for it's components. But I have an issue with type definitions for JSX
TS expects:
<Header title="Hello World" />
to be (for any react-like framework):
function Header(props: { title: string }) : JSXElement
While in this framework the actual type is based on Observables (RxJS):
function Header(props: Observable<{ title: string }>) : JSXElement | Observable<JSXElement>
E.g. a simple h1 header component:
function Header(props$) { // take in a stream of updates
return props$.pipe( // return a stream of JSX updates
map(props => <h1>{ props.title }</h1>)
);
}
So, components receive an Observable of properties and return a static JSX element or a stream of JSX elements.
UPD to clarify: The framework already works as I described, the typings is the issue. Observables are handled in the engine runtime, not in the transformation phase, so JSX transformation to createElement is fine. I just need to adjust typings for TSX, something like:
// current:
createElement<P>(fn : (props: P) => JSXElement, props: P, ...children: JSXElement[])
// should be:
createElement<P>(fn : (props: Observable<P>) => JSXElement, props: P, ...children: JSXElement[])
I see that one can partially customize JSX but I haven't found a way to override that. I've tried to override JSX.Element and JSX.ElementType w/o any visible change to typings outcome.
Is it even possible to do this override?
What types/interfaces should I override?
Thanks!
~~I'm not linking the repo not to be suspected in advertising~~
UPD: For details, you can find the framework here: http://recks.gitbook.io/
Solution
It seems like LibraryManagedAttributes can do the override I was looking for.
Preact uses it to add defaultProps to component types:
type LibraryManagedAttributes<Component, Props> = Component extends { defaultProps: infer Defaults; } ? Defaultize<Props, Defaults> : Props;
So I similarly overrode it with:
type LibraryManagedAttributes<Component, Props> =
Props extends Observable<infer O>
? O
: EmptyProps;
interface EmptyProps {}
EmptyProps is a hack to ensure components w/o proper type won't accept attributes.
It seem to work (at least, affect the types):
NOTE: as shown in this screenshot, you'll need to override built-in TS definitions for JSX via a namespace global.[LIB_NS].JSX or directly via global.JSX. For more details, see the section at the very bottom of official docs: https://www.typescriptlang.org/docs/handbook/jsx.html#factory-functions
Big thanks goes to Josep M Sobrepere for giving me a hint on preact's LibraryManagedAttributes override! 👋
Answered By - kos

0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.