Comptime.ts: compile-time expressions for TypeScript

(comptime.js.org)

143 points | by excalo 4 days ago ago

35 comments

  • spankalee 17 hours ago ago

        import {sum} from './sum.js' with {type: 'comptime'};
    
    is an unfortunate abuse of the `type` import attribute. `type` is the one spec-defined attribute and it's supposed to correspond to the mime-type of the imported module, thus the two web platform supported types are "json" and "css". The mime-type of the imported file in this case is still `application/javascript`, so if this module had a type it would be "js".

    It would have been better to choose a different import attribute altogether.

    • alpinisme 15 hours ago ago

      You’re projecting the mimetype idea from two examples but the proposal is intentionally agnostic about what type might be used for:

      > This proposal does not specify behavior for any particular attribute key or value. The JSON modules proposal will specify that type: "json" must be interpreted as a JSON module, and will specify common semantics for doing so. It is expected the type attribute will be leveraged to support additional module types in future TC39 proposals as well as by hosts.

  • apatheticonion 19 hours ago ago

    I literally just want Rust style macros and proc macros in JavaScript. e.g. using

    ``` const MyComponent = () => jsx!(<div></div>) ```

    rather than a .tsx file.

    That or wasm to be usable so I can just write my web apps in Rust

    • krukah 14 hours ago ago

      Maybe the (relative) lack of ecosystem has kept you away, but I really recommend checking out both Dioxus and Leptos. Leptos is incredibly similar to React, but with Rust ergonomics, and it's been a pleasure to learn and use. With an LLM by my side that knows React and Rust pretty well, I've found myself not even needing the React libraries that I thought I would, since I can easily build on the fly the features/components I actually need.

      I too, eventually gave up on React <> WASM <> Rust but I was able to port all my existing React over into Leptos in a few hours.

    • kitd 10 hours ago ago
    • JoelMcCracken 14 hours ago ago

      Every once in a while I get a strong urge to hack on sweet.js to add typescript support

    • sriku 13 hours ago ago

      The bun and other authors would probably do well to not repurpose already understood terminology. "Macros" are already understood to be code that produces other code. "Comptime" is a nice alternative, but bun's "macros" aren't macros in that sense.

      We had sweet-js macros as a library many years ago but it looks like it went nowhere, especially after an incompatible rewrite that (afaik) remains broken for even basic cases. (Caveat: been a while since I looked at it)

    • alpinisme 15 hours ago ago

      That particular example is odd. What are you gaining by having a macro that needs a compile step vs no macro and just configuring your compile step to use a JSX loader for js files?

      • trgwii 11 hours ago ago

        The general idea is something like prebaking computation into your deployed JS/TS. This is much more general than JSX-related tools, and a lot cheaper to run. In JS applications I often find myself doing various small bits of work on startup, comptime.ts would move all these bits into build-time.

        • alpinisme 3 hours ago ago

          Oh, I get the value of comptime! I was specifically responding to the rust-like macros comment

    • Wintamute 9 hours ago ago

      Writing a web app at the moment with C++/Emscripten. What makes wasm unusable in Rust?

    • MrBuddyCasino 5 hours ago ago

      I really really (really) don’t want Rust style macros and proc macros in JavaScript (or TypeScript), ever.

    • teaearlgraycold 18 hours ago ago

      You want manual memory management for your web apps?

      • tekacs 17 hours ago ago

        Rust memory management is... profoundly not manual?

        Case in point: I use Rust/WASM in all of my web apps to great effect, and memory is never a consideration. In Rust you pretty much never think about freeing or memory.

        On top of that, when objects are moved across to be owned by JS, FinalizationRegistry is able to clean up them up pretty much perfectly, so they're GC-ed as normal.

        • teaearlgraycold 13 hours ago ago

          Wrangling the borrow checker seems pretty manual at times. And I don’t know why you’d bother with a persnickety compile time GC when JS’s GC isn’t a top issue for front end development.

          • zdragnar 12 hours ago ago

            The borrow checker just verifies that you're handling the concept of ownership of memory correctly.

            The actual management of memory- allocating, reclaiming, etc - are all handled automagically for you.

            • auggierose 11 hours ago ago

              There is no need for the concept of ownership of memory in JavaScript. So you are wasting time on a concept that doesn't matter in languages with a real GC. Dealing with ownership = manual memory management.

              • zarzavat an hour ago ago

                You can still have ownership issues and leaks even with a GC, if an object is reachable from a root. e.g. object A is in a cache and it references object B which references objects C D E F G ... which will now never get collected.

                If A owns B then that is as expected but if A merely references B then it should hold a WeakRef

              • zdragnar 2 hours ago ago

                This used to not be true- once upon a time, Internet Explorer kept memory separate for DOM nodes and JavaScript objects, so it was very easy to leak memory by keeping reference cycles between the two.

                Now, with all the desire for WASM to have DOM access I wonder if we'll end up finding ourselves back in that position again.

  • anonymoushn 8 hours ago ago

    I have read the examples, and it seems like this cannot be used for aggressive hoisting of conditionals by writing "if (comptime foo)", resulting in the the body of the if statement being executed unconditionally or omitted. So it cannot replace my current use of C preprocessor macros in Javascript, though Zig's actual comptime feature could.

    • MrJohz 7 hours ago ago

      This can be used as part of that step (i.e. converting `foo()` into `true` or `false`), but I think the expectation is that you'll have another step in the build process that automatically strips away `if(false)...` statements and inlines `if(true)` ones.

      Almost any minifier will automatically do this, for example, and most can be configured so that they only do constant folding/dead code elimination, so the result will be a file that looks like the one you've written, but with these comptime conditions removed/inlined.

      Obviously with C preprocessor macros, you've got one tool that evaluates the condition and removes the dead code, but with comptime you have more flexibility and your conditions are all written in Javascript rather than a mix of JS and preprocessor macros.

    • trgwii 7 hours ago ago

      Yes it can, the comptime expression inside the if would turn into a `true` or `false` literal, but you would need a separate build tool to optimize away the if. That's partly why comptime.ts outputs TypeScript iirc.

      I believe both Vite and Bun bundler would apply the optimization to eliminate constant conditionals when you use comptime.ts as a plugin.

  • stevage 18 hours ago ago

    I could imagine this being useful for pre-compiling markdown.

  • mdarens 19 hours ago ago

    One of the most exciting features of Zig, but am I correct that this doesn’t apply to types themselves like comptime generics in Zig? I find that to be one of the most powerful ideas: type level mappings that have the same syntax as the runtime code where you can just set an iteration limit. This would be a great way to get around the “too large union” problem in TS, for example.

  • Thom2000 21 hours ago ago

    Interesting. I've never seen the import-with syntax, though and it's hard to find any documentation on it. Is this a syntax extension?

  • shortrounddev2 15 hours ago ago

    Would be really great if it could return named functions

    • trgwii 11 hours ago ago

      I have had many discussions with the author and we ultimately decided not to support those kinds of usecases until we have a very solid set of guarantees. Supporting closures can quickly become very tricky when you need to preserve a function across JS processes.

      • shortrounddev2 2 hours ago ago

        I just want to be able to select dependencies at bundle time depending on the build environment. If its in dev, use `MockService`. If its in prod, use `ProdService`. Right now I just have `index.prod.ts` and `index.dev.ts` that choose the dependencies, which is not a bad solution, I just wish I could keep my initialization code in one file and have functions return the dependencies based on the environment. I can do this at runtime obviously but it doesnt seem to eliminate unused dependencies well

        I know its a cursed idea but I often find myself wishing typescript had a C++ style preprocessor

  • revskill 13 hours ago ago

    Sweet. No need a framework to do that.