Please note this is NOT a reactjs question.
I have happily implemented my own jsx factory, and convinced TypeScript to typecheck my attributes:
declare global {
namespace JSX {
interface ElementAttributesProperty {
opts;
}
}
}
Now, my toolkit uses a fairly trivial component type:
export abstract class Component<T, U extends HTMLElement> {
opts: T;
constructor(opts: T) { };
elem: U;
}
I can even go further, my jsxFactory method (named h) can be typed as follows:
export interface ComponentConstructor<T, U extends HTMLElement, K extends Component<T, U>> {
new(props: T): K
}
function h<T,U extends HTMLElement, K extends Component<T,U>>(tag: ComponentConstructor<T,U,K>, props: T, ...children: any[]): K;
function h<K extends keyof HTMLElementTagNameMap>(tag: K, props: any, ...children: any[]): HTMLElementTagNameMap[K];
So this allows me to correctly infer the return type for h(Button, { label: "foo"}) as Button.
However, TypeScript's jsx inference always returns "JSX.Element", which according to the docs:
By default the result of a JSX expression is typed as any. You can customize the type by specifying the JSX.Element interface. However, it is not possible to retrieve type information about the element, attributes or children of the JSX from this interface. It is a black box.
This reads to me as "any jsx expression will always return the exact type, JSX.Element", but if so, see my request for information at the end of this question.
So in my example:
let btn = <Button label="foo"/> // types as JSX.Element = any :(
Given the ideal behaviour of h's type declaration, this feels unsatisfactory- is there a way to have TypeScript respect the the same behaviour as my h? (i.e. infer that btn above is of type Button.) If not, why is it not possible? In particular why doesn't TypeScript use the definition of my jsxFactory method rather than force me to use this JSX namespace magic in the first place?