Skip to content

Support .tsx file extensions. #56322

Closed as not planned
Closed as not planned

Description

@cpojer

What is the problem this feature will solve?

Node's --experimental-strip-types and --experimental-transform-types are great improvements that allow us to drop a lot of tooling to run TypeScript in node. Right now, it only supports the .ts extension and does not work on .tsx. .tsx only really exists because of syntax ambiguity with a (somewhat legacy) way of typecasting in .ts files: microsoft/TypeScript#26489 (comment)

Why would you use .tsx for backend code?

In a monorepo, you might have many packages shared by the client and backend. For consistency, those projects may want to use only a single file extension like .tsx. If you use React, which many people do, and some files use .ts and some .tsx, it's always jarring to type JSX in a .ts file, realize it's a syntax error, and then having to rename the file. The renames also make it harder to navigate git history. Finally, if you are using only a single extension, TypeScript's behavior is always the same with regards to how angle brackets work.

I'd be excited to implement this feature and send a Pull Request if the Node.js team is in favor of supporting .tsx extensions.

What is the feature you are proposing to solve the problem?

I imagine the most workable solution is to support .tsx extensions just like .ts, but throw an error if JSX is actually used. I'm not advocating for supporting JSX, just for node to support .tsx files without compiling JSX.

What alternatives have you considered?

The alternatives are to keep using third-party tooling to strip TypeScript types, or to rename all backend files using .tsx to .ts, which may not be possible in large monorepos given the amount of files affected, and as mentioned previously, makes git history more cumbersome to work through.

Activity

  1. aduh95 commented on Dec 20, 2024

    @aduh95
    Contributor

    Would you like to send a PR?

  2. cpojer commented on Dec 20, 2024

    @cpojer
    Author

    Before working on it, I would like to discuss and reach consensus that the Node.js maintainers would like to include this feature.

  3. aduh95 commented on Dec 20, 2024

    @aduh95
    Contributor

    I'm not sure what there is to discuss, but in any case, it won't happen until someone submits a PR.

    //cc @nodejs/typescript

  4. cpojer commented on Dec 20, 2024

    @cpojer
    Author

    I see, so from my perspective here is what I think we need consensus on:

    • Does Node.js want to support .tsx?
    • If yes, is it acceptable (at least as a first step), to support the .tsx extension only, without actually supporting JSX? See https://www.typescriptlang.org/tsconfig/#jsx for the complexity involved with supporting JSX.

    imho this would solve the outlined problem for most users, but some might have a desire to support JSX directly in the future in some form.

  5. JakobJingleheimer commented on Dec 20, 2024

    @JakobJingleheimer
    Member

    Many of the arguments cited in favour seem illogical to me. It seems like you're suggesting supporting a mismatched file extension and then erroring when the file extension is used correctly?

    Supporting actual TSX would be quite a hairy ordeal, and encouraging people to misuse the .tsx extension seems like a bad idea.

    Or have I misunderstood you?

  6. cpojer commented on Dec 20, 2024

    @cpojer
    Author

    Yes, you misunderstood. Using the .tsx extension without using JSX is not a misuse of TypeScript. Why would it be?

  7. JakobJingleheimer commented on Dec 20, 2024

    @JakobJingleheimer
    Member

    It seems I did not. That is a misuse of the file extension, and it would be a landmine for users. We should absolutely not do that.

    However, if we were to support jsx and thus tsx, that would be a different story. One would need to figure out how to handle configuration, because jsx has multiple transpile options. I could see value in that, but I would advise first sketching out some broad-strokes before jumping to a PR (which would almost certainly result in contradicting opinions). Far better to get those to consensus before churning on an implementation. Happy to answer questions for that 🙂 I don't know the appetite of others (I'm not sure of my own opinion on it) to support jsx though.

  8. ljharb commented on Dec 20, 2024

    @ljharb
    SponsorMember

    How is it not a misuse of a file extension to do that? That's what .jsx exists for, for when you have jsx and only when you have jsx, and I'd expect ts/tsx to be the same thing. If there's no jsx in it, it's just JS or TS, and the file extension should reflect that accurately.

  9. cpojer commented on Dec 20, 2024

    @cpojer
    Author

    There are many projects with files that use the .tsx extension without using JSX. Tools like ts-node support it and TypeScript does not throw an error if it cannot find a JSX AST node in a .tsx file. It seems to me like .tsx is well-supported by TypeScript unlike using a random extension like .definitelynottypescript. Maybe I'm missing something, but do you have a source where the TypeScript team states it is a misuse of the extension if not JSX nodes are present?

    On the topic of partial support, Node.js implemented --experimental-strip-types without support for TypeScript features like enums until --experimental-transform-types was added. Why can we not take a similar path for this?

  10. GeoffreyBooth commented on Dec 20, 2024

    @GeoffreyBooth
    Member

    It seems to me that supporting files with a .tsx extension but erroring on JSX syntax within would seem very broken to users. Sure it’s possible to write a .tsx file with no JSX syntax, just as it’s possible to write a .ts file with no TypeScript syntax, but I don’t think that’s what most users would consider “.tsx support.”

    On the topic of partial support, Node.js implemented –experimental-strip-types without support for TypeScript features like enums until –experimental-transform-types was added. Why can we not take a similar path for this?

    This proposal is more like saying “why not support the .ts extension but only if such files contain only JavaScript syntax.”

  11. marco-ippolito commented on Dec 20, 2024

    @marco-ippolito
    Member

    I explored this idea but I'm against it.
    The main idea behind TypeScript support in Node is the type stripping. tsx cannot be run once types are stripped or syntax is transformed, it always requires an external loader.
    Maybe, (maaaaaaybe) adding support in module.stripTypeScriptTypes would be "acceptable".

  12. cpojer commented on Dec 20, 2024

    @cpojer
    Author

    @marco-ippolito:

    tsx cannot be run once types are stripped or syntax is transformed, it always requires an external loader.

    Would you mind elaborating on this sentence?

  13. marco-ippolito commented on Dec 20, 2024

    @marco-ippolito
    Member

    @marco-ippolito:

    tsx cannot be run once types are stripped or syntax is transformed, it always requires an external loader.

    Would you mind elaborating on this sentence?

    If types are removed, all its left is jsx that cannot be run without an external loader or a framework (that uses a loader)

  14. arthurfiorette commented on Dec 20, 2024

    @arthurfiorette

    The logical approach, considering the previously discussed --experimental-strip-types and --experimental-transform-types flags, is to introduce a new one: --experimental-jsx. This flag should include support for <jsx /> syntax and only it. Alongside it, just add .tsx as a valid ts extension and only strip types out of it,

    While I agree with adding support for .tsx files without JSX, this "feature" should only be merged once Node.js can reliably execute JSX code.

    Introducing a new flag might seem exhaustive, but it aligns with the established decision-making process. Moreover, the JSX implementation should remain independent of TypeScript-specific features, much like how .jsx files can be used alongside a jsconfig.json file.

    This involves two distinct steps:

    1. Process .tsx files just as .ts files to strip their types.
    2. Properly handle .js and .jsx files that include <jsx /> syntax.

    Providing only one of these features without the other is both counterintuitive and likely to confuse users.

  15. arthurfiorette commented on Dec 20, 2024

    @arthurfiorette

    A potential first step toward this approach could be to simply ignore <jsx /> syntax entirely while adding support for the .tsx extension.

    If this change is released with a clear note explaining that it currently ignores <jsx /> syntax, it could also recommend Node.js users rely on a custom loader like @educandu/node-jsx-loader for handling JSX in the interim.

    This would provide a practical and usable solution much earlier, giving developers a path forward without having to wait for the complete implementation of a --experimental-jsx flag. That flag, once introduced, could be entirely independent of the Node+TypeScript-specific features. It would focus solely on transforming JSX syntax, regardless of whether TypeScript is in use.

  16. marco-ippolito commented on Dec 20, 2024

    @marco-ippolito
    Member

    I'd first try to get consensus for jsx support, if that happens adding type stripping to tsx is easy.

  17. arthurfiorette commented on Dec 20, 2024

    @arthurfiorette

    The opposite would allow jsx usage via custom loaders, waiting for jsx support means no jsx at all until that + it would bring more people to use --experimental-strip-types and --experimental-transform-types which I think is better.

  18. GeoffreyBooth commented on Dec 20, 2024

    @GeoffreyBooth
    Member

    JSX has (at least) two issues that would need addressing:

    1. It requires transformation, like transform-types.
    2. The transformation requires configuration, for example to specify React or Vue or something else.

    JSX isn’t only used by React. For example, this JSX:

    export default function Test() {
      return (
        <h1>Hello World!</h1>
      )
    }

    compiles per this site into this JavaScript for React:

    export default function Test() {
        return /*#__PURE__*/ React.createElement("h1", null, "Hello World!");
    }

    and this for Vue:

    import { createVNode as _createVNode, createTextVNode as _createTextVNode } from "vue";
    export default function Test() {
      return _createVNode("h1", null, [_createTextVNode("Hello World!")]);
    }
  19. arthurfiorette commented on Dec 20, 2024

    @arthurfiorette

    JSX indeed has specific targets, primarily react and react-jsx, with distinct output patterns for the same JSX input. For example:

    <h1>Hello world</h1>

    react-jsx:

    import { jsx as _jsx } from "react/jsx-runtime";
    _jsx("h1", { children: "Hello world" });

    react:

    import React from "react";
    React.createElement("h1", null, "Hello world");
    • jsxFactory:
      Specifies the function to replace createElement when the react target is used. For example, setting jsxFactory: 'h' changes React.createElement to h.

    • jsxFragmentFactory:
      Determines the component used for fragments (<></>). If set, fragments will use the specified name instead of React.Fragment.

    • jsxImportSource:
      Used with the react-jsx target to change the imported package. For example, jsxImportSource: 'preact' would generate:

      import { jsx as _jsx } from "preact/jsx-runtime";

    The combination of them is what is done for any library that supports JSX, such as Vue.js, Kita, React, Preact, or Astro.

  20. ljharb commented on Dec 20, 2024

    @ljharb
    SponsorMember

    It seems great to support jsx syntax somehow, but the closest "default" transformation of jsx is for react, and i'm not sure that's what everyone would want.

    As for

    For consistency, those projects may want to use only a single file extension

    I think this is just an incorrect "want" - it's not like their images and JSON files are going to use the same extension, because the point of a file extension is 0% aesthetics and 100% accurately conveying the parse goal of the file.

  21. JakobJingleheimer commented on Dec 20, 2024

    @JakobJingleheimer
    Member

    The opposite would allow jsx usage via custom loaders, waiting for jsx support means no jsx at all until that + it would bring more people to use --experimental-strip-types and --experimental-transform-types which I think is better.

    This exists. @nodejs-loaders/tsx, ts-node, and others.

    This discussion is going in circles. You are not addressing the problems this proposal creates, despite us explaining them in multiple different ways.

    TS cavorting as TSX is a hard "no" by many. In the interest of sparing valuable time: I will block this (as others have indicated they will too).

    JSX support is a maybe, dependent on design. I am happy to help you outline a possible plan forward, although I do not necessarily support it—I share Marco's concerns: the current implementation is brilliant in its simplicity, and this seems contrary and counter to that.

    If you wish to pursue supporting JSX, please open a new issue. If you would like my help with that, please tag me.

  22. sibelius commented on Dec 29, 2024

    @sibelius

    what is the workaround for this right now ?

  23. JakobJingleheimer commented on Dec 29, 2024

    @JakobJingleheimer
    Member

    Please see the comment immediately above 🙂

  24. sibelius commented on Dec 29, 2024

    @sibelius

    @nodejs-loaders/tsx is the best option right now ?

  25. JakobJingleheimer commented on Dec 29, 2024

    @JakobJingleheimer
    Member

    I'm the author of @nodejs-loaders/tsx, so I'm biased. AFAIK, I created nodejs-loaders (the previous incarnation of the @nodejs-loaders collection) before ts-node had a loader. I haven't used ts-node because @nodejs-loaders/tsx worked great for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature requestIssues requesting new Node.js features.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions