[HN Gopher] Show HN: Zero-codegen, no-compile TypeScript type in...
       ___________________________________________________________________
        
       Show HN: Zero-codegen, no-compile TypeScript type inference from
       Protobufs
        
       Author : 18nleung
       Score  : 76 points
       Date   : 2025-04-14 15:41 UTC (7 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | jillyboel wrote:
       | Cool, but I assume not great for performance?
       | 
       | Probably better to just stick with codegen
        
         | dtech wrote:
         | Assuming you mean compiler/editor performance then yes I assume
         | this wrecks it. Shouldn't matter for runtime though.
        
           | jillyboel wrote:
           | Right, I meant editor performance. None of the options should
           | impact runtime performance anyway.
        
         | 18nleung wrote:
         | You're right that IDE/dev-time performance might be slower than
         | using generated types since this relies on "dynamic" TypeScript
         | inference rather than static codegen'd types.
         | 
         | That said, depending on how your codegen works and how you're
         | using protos at runtime, this approach might actually be faster
         | at runtime. Types are stripped at compile-time and there's no
         | generated class or constructor logic -- in the compiled output,
         | you're left with plain JS objects which potentially avoids the
         | serialization or class overhead that some proto codegen tools
         | introduce.
         | 
         | (FWIW, type inference in VSCode seemed reasonably fast with the
         | toy examples I was playing with)
        
           | recursive wrote:
           | Typescript never generates classes or constructors that
           | aren't present in source code. Whether or not constructors
           | are present is completely independent from whether you're
           | using code gen.
        
           | jillyboel wrote:
           | > depending on how your codegen works and how you're using
           | protos at runtime, this approach might actually be faster at
           | runtime
           | 
           | If your codegen is introducing runtime overhead you should
           | use a different codegen.
           | 
           | > type inference in VSCode seemed reasonably fast with the
           | toy examples I was playing with
           | 
           | It usually is. It can become a problem in a real project that
           | has a _lot_ of stuff going on, though.
        
       | mubou wrote:
       | The fact that the source is so small is wild. I would have
       | expected a huge convoluted parsing library implemented in types.
       | 
       | On the other hand, the fact that this is even possible is more
       | wild. Instead of replacing JS with a proper statically-typed
       | language, we're spending all this effort turning a preprocessor's
       | type system into a turing-complete metalanguage. Pretty soon
       | we'll be able to compile TypeScript entirely using types.
        
         | 18nleung wrote:
         | I would have written a shorter source, but I did not have the
         | time.
        
         | sgrove wrote:
         | Or even run doom in TypeScript's type system!
        
           | mubou wrote:
           | Prepare to have your mind blown:
           | 
           | https://www.youtube.com/watch?v=0mCsluv5FXA
        
             | IshKebab wrote:
             | Probably not though because he was clearly referring to
             | that.
        
         | throwanem wrote:
         | People have fussed the same of the C preprocessor, around the
         | same time I and maybe you were born. (There's a pretty good
         | chance I'm your parents' age, and nearly no chance you're the
         | age of mine.)
        
           | NoTeslaThrow wrote:
           | The criticisms were valid then, too. C (including the
           | preprocessor of course) is still not fully parseable if you
           | include things like token concatenation.
        
             | throwanem wrote:
             | I make no representation as to soundness, then or now. Not
             | till I figure out where my copy of the UNIX-HATERS Handbook
             | has got to, at any rate. I've had cause reasonably recently
             | to reread the X and sendmail chapters, not so much this
             | one.
        
         | plopz wrote:
         | I wish javascript had gone in the same direction as php with
         | types.
        
           | rad_gruchalski wrote:
           | Which is?
        
             | 1oooqooq wrote:
             | literary anything other than ts with it's hacks to half
             | assely support multiple signatures that end up crippling
             | the whole thing. there's zero type inference anywhere
             | unless you specifically import types and manually assign
             | everything.
             | 
             | coming from rust, ts has a very high f-meter score
        
               | rad_gruchalski wrote:
               | I was hoping to hear how are php types better than TS
               | instead of another rant about how Rust is the greatest.
               | Anyone?
               | 
               | By the way, having lived in Scala 2 for a few years, Rust
               | is half-assed. Type system leaks through the fingers when
               | working with async collections. Future.sequence from
               | Scala makes great cli apps. Scala collections are a work
               | horse. The only thing from Rust I like is the shorthand
               | question mark return (compiler magic for Result type).
        
         | spankalee wrote:
         | TypeScript does an amazing job at describing the types of real-
         | world JavaScript. It's incredibly good, and very useful, even
         | in the face of extremely dynamic programs. The fact that it can
         | describe transforms of types, like "this is a utility that adds
         | an `xxx` prefix to every property name" is frankly unparalleled
         | in mainstream languages, but more importantly lets us describe
         | patterns that come up in real-world JS programs - it's not
         | fluff!
         | 
         | And luckily, the most complex of types are usually limited to
         | and contained within library type definitions. They add a lot
         | of value for the library users, who usually don't have to deal
         | that that level of complexity.
        
           | mubou wrote:
           | I don't disagree! It's just the fact that it has to be
           | transpiled to JS that's the problem, because it means none of
           | the types are "real"; there's no runtime assurance that a
           | string is actually a string. TS is great and I'd never go
           | back to JS, but it's ultimately a bandaid. Native TS support
           | in browsers is probably never going to happen, though, sadly.
           | 
           | Imagine if WASM were supported natively instead, with
           | browsers exposing the same DOM interfaces that they do to JS.
           | You could link a wasm binary in a <script> and do everything
           | you can with JS/TS, but with any language of your choosing.
           | No doubt a compiled form of TS would appear immediately. We'd
           | no longer need separate runtime type checking.
           | 
           | Just feels like priorities are in the wrong place.
        
             | merb wrote:
             | Wasm gc was needed for that. Wasm evolves slowly so that it
             | can be done right. Even if the dom api comes, not a lot of
             | it will change since only c-like languages will be as small
             | as possible to fit into the space of JavaScript.
        
             | spankalee wrote:
             | I think you're conflating cause and effect in several
             | cases. TypeScript can't be thought of, and would never
             | exist, independently from JavaScript like you're trying to
             | do.
             | 
             | TypeScript wasn't created separate from JavaScript and then
             | chose JavaScript as a backend. TypeScript only exists to
             | perform build-time type checking of JavaScript. There
             | wouldn't be a TypeScript that compiled to something else,
             | because other languages already have their own type
             | systems.
             | 
             | Runtime type-checking isn't part of TypeScript because 1)
             | It isn't part of JavaScript, and TypeScript doesn't add
             | runtime features anymore. 2) It'd be very expensive for
             | simple types, 3) Complex types would be prohibitively
             | expense as you have to both reify the types and perform
             | deep structural checking.
             | 
             | WASM also is natively supported, and with newer extensions
             | like reference types and GC, we're getting closer to the
             | point where a DOM API could be defined. It'll still be a
             | long while, but that's the long-term direction it's heading
             | in. But even then, you would only see a TypeScript-to-WASM
             | compiler[1] because there's already so much TypeScript out
             | there, not because TypeScript is a particularly good
             | language for that environment. A more static language would
             | be a lot better for a WASM target.
             | 
             | [1]: Porfor is already such a compiler for JS and TS, but
             | it does not do runtime type-checking: https://porffor.dev/
        
       | tim1994 wrote:
       | It's always impressive how far people can take TypeScript and
       | even build parsers with it. But this is limited to inlined string
       | literals and cannot read files (a TS limitation).
       | 
       | I wonder if the author has a use case in mind for this that I
       | don't see. Like if you are only using TS, what's the point of
       | protobuf? If you are exchanging data with programs written in
       | other languages why avoid the protobuf tooling that you need
       | anyway?
       | 
       | Maybe this is just a fun toy project to write a parser in TS?
        
       | throwanem wrote:
       | I love this, and I bet the compile errors it produces on
       | malformed protobuf are _wild._
        
       | porridgeraisin wrote:
       | Cool. Once the linked TS issue is resolved it will be able to
       | resolve from files too which is great
        
       | recursive wrote:
       | This requires the whole `.proto` declaration inline in source a
       | string constant. I'm not holding my breath on "Import non-js
       | content"[1] getting approved, so that means you still have to use
       | another build dependency, or manually keep the .proto files
       | synchronized across multiple sources truth. In that light, it's
       | not clear when this would be a benefit over straight-forward code
       | gen. Cool POC hack though.
       | 
       | [1]: https://github.com/microsoft/TypeScript/issues/42219
        
         | cadamsdotcom wrote:
         | That can be done with a `sed` call so it's not a new
         | dependency.
        
         | ZitchDog wrote:
         | The problem is that TypeScript is terrible at codegen, there
         | are no standard extension points like we have with javac and
         | others. So we are forced to do these crazy hacks at the type
         | level rather than just generating types as you would in other
         | languages.
        
           | recursive wrote:
           | Not familiar with the capabilities of javac, but in my
           | imagination, I'm referring to a tool that runs prior to the
           | typescript compiler, that just writes the intended source as
           | text. Typescript never knew it wasn't in the repository or
           | anything.
        
         | catapart wrote:
         | It's true that it's another dependency, but this is the entire
         | contents of a file I drop into my project root called `raw-
         | loader.d.ts`:
         | 
         | ```
         | 
         | declare module '*?raw' { const rawFileContent: string export
         | default rawFileContent }
         | 
         | ```
         | 
         | Then, when I add the file to my types property array of my
         | tsconfig's compilerOptions, I can import anything I want into a
         | typescript file as a string, so long as I add "?raw" to the end
         | of it. I use it to inject HTML and CSS into templates. No
         | reason it couldn't be used to inject a .proto file's contents
         | into the inline template.
         | 
         | Again, you're technically correct! But a "import non js
         | content" feature is a pretty solveable problem in TS. Maybe not
         | at the language level, but at the implementation level, at
         | least.
        
       | spankalee wrote:
       | This would be even nicer if TypeScript added type inference for
       | tagged template literals, like in this issue [1]. Then you could
       | write:                   const schema = proto`           syntax =
       | "proto3";                message Person { ... }         `;
       | type Person = typeof schema['Person'];
       | 
       | And you could get built-in schema validation with a sophisticated
       | enough type definition for `proto`, nice syntax highlighting in
       | many tools with a nested grammar.
       | 
       | We would love to see this feature in TypeScript to be able to
       | have type-safe template in lit-html without an external tool.
       | 
       | The issue hasn't seen much activity lately, but it would be good
       | to highlight this library as another use case.
       | 
       | [1]: https://github.com/microsoft/TypeScript/issues/33304
        
       | aappleby wrote:
       | This is both hilarious and awesome. I think the Typescript devs
       | are just showing off at this point. :D
        
       | meindnoch wrote:
       | Looks like TypeScript envies Swift's compile times.
        
       | pragma_x wrote:
       | What's kind of amazing is that Typescript's matching of strings
       | through the type system converges on a first-class PEG in a few
       | places (see string.ts). The rest of the library is really damn
       | succinct for how much lifting it's doing.
       | 
       | My hat's off to the author - I attempted something like this for
       | a toy regex engine and got nowhere fast. This is a much better
       | illustration of what I thought _should_ be possible, but I
       | couldn't quite wrap my head around the use of the ternary
       | operator to resolve types.
        
       | catapart wrote:
       | Very cool work!
       | 
       | Also, I hope you expected me to read that output in the same
       | cadence as the Hooli focus groups, because that's exactly what I
       | did.
        
       | mherkender wrote:
       | This is kinda why I hate advanced type systems, they slowly
       | become their own language.
       | 
       | "No compile/no codegen" sounds nice until you get slow compile
       | times because a type system is slow VM, the error messages are so
       | confusing it's hard to tell what's going on, and there's no
       | debugging tools.
        
       | jitl wrote:
       | It's pretty rad how flexible template literal types are, but I
       | can't imagine wanting this kind of shenanigans hanging out in a
       | production app slowing down compile times. I prefer to define
       | types in TypeScript and generate proto from that, since the
       | TypeScript type system is so much more powerful than the Protobuf
       | system. Types are much more composable in TS.
        
         | tantalor wrote:
         | What do you use to go from ts->pb?
        
       | mifydev wrote:
       | This makes me wonder if this the way to do schema generation in
       | Typescript. I'm working on Typeconf, and we have a separate step
       | for translating Typespec schema to Typescript, it'll be cool if
       | we could just load typespec directly.
        
       ___________________________________________________________________
       (page generated 2025-04-14 23:00 UTC)