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/
To override a style, you have two options. Either you can pass a style object, or you can pass a function that will obtain props regarding the current internal state of the component, thus enabling you to modify styles dynamically depending on the component state, such as isError or isSelected .
TypeScript supports embedding, type checking, and compiling JSX directly to JavaScript.
To change state in a class component use the setState method. This method enqueues changes in the state and causes a component to re-render. Set Another Value!
Your "JSX" templating is mixed directly into your JavaScript file and component. TSX is the TypeScript flavor of JSX, with file extensions ending in .
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! 👋
Please, feel free to add your answer if you know more details
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With