TransWikia.com

JSX TypeScript definitions override for custom framework's components?

Stack Overflow Asked by kos on January 2, 2021

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/

2 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

Correct answer by kos on January 2, 2021

Try overriding and customizing node_modules/@types/react/index.d.ts and node_modules/@babel/types/lib/index.d.ts I was able to set React.FC to the type {foo: 'bar'} and also update the JSXElement to have the same type, and then write a component that returns that object & renders it without producing a type error. I am testing by modifying the typings in a React project. I didn't test with your library, but I think the key is to not load the React typings and instead load your own typings. The fact the typings are effectively global is the larger issue, consumers of your project may need separate tsconfig files to use both libs in the same project with this approach.

Answered by Josh Ribakoff on January 2, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP