Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSX TypeScript definitions override for custom framework's components?

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/

like image 994
kos Avatar asked Sep 16 '20 17:09

kos


People also ask

How do you override existing React components?

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 .

Can we use JSX in TypeScript?

TypeScript supports embedding, type checking, and compiling JSX directly to JavaScript.

How do you update the state of a component with a TypeScript class?

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!

What is difference between JSX and TSX?

Your "JSX" templating is mixed directly into your JavaScript file and component. TSX is the TypeScript flavor of JSX, with file extensions ending in .


1 Answers

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):

code example

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

like image 67
kos Avatar answered Oct 06 '22 00:10

kos