Just read the changelog for TypeScript 2.8 and they finally allow mixing different JSX namespaces. This improves interoperability when preact is used alongside react components.
Note that we should not release this until TS 2.8 is out.
TS 2.8 Changelog: https://blogs.msdn.microsoft.com/typescript/2018/03/15/announcing-typescript-2-8-rc/
Nice - I saw a Twitter thread about this last week. Is there anything we can do to account for the JSX.Element change?
FYI: TypeScript 2.8 was just released earlier today 🎉 Will tackle this issue sometimes in the next days.
Just gave this a go, but to properly support this users will have to change their code.
before:
// tsconfig.json
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "h",
}
}
import { h, Component } from "preact";
export class Foo extends Component {
render() {
return <div>hey</div>;
}
}
after:
// tsconfig.json
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "preact.h",
}
}
// wildcard import is needed
import * as preact from "preact";
export class Foo extends preact.Component {
render() {
return <div>hey</div>;
}
}
But before proceeding I'd love to see if there is a better option. Filed an issue over at the TypeScript: https://github.com/Microsoft/TypeScript/issues/23762
@andrewiggins I'd love to here your opinions on this. My guess is that we'll have to advice users to switch to wildcard exports sooner or later once more JSX-specific features like Fragments land.
List of exports:
h / createElementJSX namespace (TS 2.8)Fragment (not implemented yet)Yea, that makes sense. If we require TS users to use the wildcard syntax over the destructed syntax, perhaps we make this typings change as part of the 9.0 release (if one is planned)?
I'm a bit on the fence if we should mark this as breaking or not. From the TS user perspective this is the right thing to do, but most of our users don't use TS but rely on plain js.
Perhaps we can ease the transition to a wildcard-like syntax by exposing JSX through both top-level and under h. I'll check if this is a feasible thing to do.
It should be doable. Wesley from the TypeScript suggested to do that in the issue you opened.
The question I still have is how would Fragment work. Does it need to be exported on the same object JSX is exported? I haven't played with fragments at all yet though so I need to learn more about them (and check out your PR)
@andrewiggins yep, the Fragment definition has to be exported on the JSX object. The fragments PR is still a work in progress, though :)
Any update on this? Or are there any decent workarounds I can make on my side if I want to use react and preact in the same project?
All the workarounds I can think of seem quite messy. Seems like I will need to move the preact code out to have a separate package.json.
Edit: Ah, now I see in another issue (of course right after I decide to post) that we can remove the react typings to get it compiling without errors. I'm sure there's some mismatches between the two JSX typings (allowing class is the obvious one), so I'll be quite pleased when this is resolved. If theres another workaround, I'd love to hear that as well.
Wouldn't "reactNamespace": "preact" work?
@cromefire reactNamespace is deprecated and replaced by jsxFactory. The problem with either is that they only have an effect code-generation and not types which are checked before any code is generated.
We're desperate to try out preact-compat in our TS/React project, now that createRef has been implemented in the latest update I'd love to hear if there were any decent solutions to this issue
Related: https://github.com/developit/preact-compat/issues/357
Sorry bringing this up once again and maybe I'm wrong, but I think JSX must remain outside preact namespace. Rewriting all JSX messy references to preact.JSX fixes the problem.
Code generated with:
douglas:~/labs
$ preact create typescript preact-x --yarn && cd preact-x
douglas:~/labs/preact-x
$ yarn add preact@500 preact-router@500
packages:
douglas:~/labs/preact-x
$ cat package.json | grep -E '^\s+"preact'
"preact-cli": "^3.0.0-next.19",
"preact-render-spy": "^1.3.0",
"preact": "^10.0.0-beta.0",
"preact-router": "^3.0.0"
douglas:~/labs/preact-x
$
douglas:~/labs/preact-x
$ yarn build
yarn run v1.15.2
$ preact build
Build [=================== ] 95% (2.3s) emitting
ssr-bundle.ed2dc.css ⏤ 569 B (+569 B)
ssr-bundle.js ⏤ 7.07 kB (+7.07 kB)
✖ ERROR ./node_modules/preact-render-spy/index.d.ts
ERROR in ./node_modules/preact-render-spy/index.d.ts(98,23):
TS2503: Cannot find namespace 'JSX'.
✖ ERROR ./node_modules/preact-render-spy/index.d.ts
ERROR in ./node_modules/preact-render-spy/index.d.ts(121,18):
TS2503: Cannot find namespace 'JSX'.
✖ ERROR ./node_modules/preact-render-spy/index.d.ts
ERROR in ./node_modules/preact-render-spy/index.d.ts(127,18):
TS2503: Cannot find namespace 'JSX'.
✖ ERROR ./node_modules/preact-router/index.d.ts
ERROR in ./node_modules/preact-router/index.d.ts(65,58):
TS2503: Cannot find namespace 'JSX'.
✖ ERROR ./node_modules/preact-router/match.d.ts
ERROR in ./node_modules/preact-router/match.d.ts(9,36):
TS2503: Cannot find namespace 'JSX'.
✖ ERROR ./src/components/header/index.tsx
ERROR in ./src/components/header/index.tsx(11,22):
TS2322: Type '{ children: string; activeClassName: string; href: string; }' is not assignable to type 'LinkProps'.
Property 'children' does not exist on type 'LinkProps'.
✖ ERROR ./src/components/header/index.tsx
ERROR in ./src/components/header/index.tsx(14,22):
TS2322: Type '{ children: string; activeClassName: string; href: string; }' is not assignable to type 'LinkProps'.
Property 'children' does not exist on type 'LinkProps'.
✖ ERROR ./src/components/header/index.tsx
ERROR in ./src/components/header/index.tsx(17,22):
TS2322: Type '{ children: string; activeClassName: string; href: string; }' is not assignable to type 'LinkProps'.
Property 'children' does not exist on type 'LinkProps'.
✖ ERROR undefined
Done in 5.91s.
@douggr Looks like the typings for preact-render-spy need to be updated. Can you file an issue over there?
EDIT: if you're looking for enzyme-like testing capabilities we provide an officially endorsed enzyme adapter for that: https://github.com/preactjs/enzyme-adapter-preact-pure
Couldn't one alias preact.JSX as JSX?
@marvinhagemeister thank you.
I think preact-render-spy is kinda abandoned actually. But will do.
Unfortunately this issue also happens with preact-router@3.
I encountered this issue with the type definitions for enzyme (@types/[email protected]), which refer to JSX.Element and JSX.HTMLAttributes in a few places. I was able to hack around it by aliasing just those two types in my own type declaration file:
declare namespace JSX {
type Element = preact.JSX.Element;
type HTMLAttributes = preact.JSX.HTMLAttributes;
}
That was enough for me, and I'm only using Preact so I don't have the concerns of mixing React and Preact expressed as part of this issue. Does anyone know how others projects _should_ reference JSX, so as to be agnostic to the actual provider of that type? Are there any examples from modules that work with both React and Preact X?
You'll probably want to install @types/react for that
Can someone please explain how one uses React in one file, and Preact in another file? How does TypeScript know which JSX types to use per file?
EDIT: looks like that's done with /* @jsx h */ at the top of Preact files (as long as h is imported and available in the module scope), and otherwise letting other files fall back to React's global JSX.
@trusktr The /* @jsx */ comment is the best workaround so far. The main issue is that the React JSX types are declared globally and therefore take precedence over anything else.
It turns out the new TypeScript Project References feature is great for solving the namespace clashing. Under the premise that Preact defines JSX types globally, we can put Preact components in one folder and don't import React in any code there, and put React components in another folder and don't import Preact there, define each folder as a project reference, and finally observe the globals will be segregated on a per-project basis. Preact code will only see Preact JSX, and React code React JSX.
I've been using this technique to use multiple types of JSX components in one overall project, with jsx: preserve, and a varying compile step for JSX depending on which folder a file is in.
Most helpful comment
Any update on this? Or are there any decent workarounds I can make on my side if I want to use react and preact in the same project?
All the workarounds I can think of seem quite messy. Seems like I will need to move the preact code out to have a separate package.json.
Edit: Ah, now I see in another issue (of course right after I decide to post) that we can remove the react typings to get it compiling without errors. I'm sure there's some mismatches between the two JSX typings (allowing class is the obvious one), so I'll be quite pleased when this is resolved. If theres another workaround, I'd love to hear that as well.