[HN Gopher] TypeScript please give us reflection/runtime types
       ___________________________________________________________________
        
       TypeScript please give us reflection/runtime types
        
       Author : decs
       Score  : 171 points
       Date   : 2023-07-07 19:36 UTC (3 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | dontlaugh wrote:
       | Maybe controversial, but I think code generation is fine. It work
       | well enough in Go or C#.
        
       | vbezhenar wrote:
       | The one downside of this proposal is faster typescript processors
       | like esbuild. Right now they don't care about types, they just
       | strip them from the code. It allows them to be very fast and
       | provide fast feedback loop.
       | 
       | With this proposal those tools must become extremely more
       | complicated and probably they will just be as slow as tsc.
        
       | shadowgovt wrote:
       | Normally I'd say "hard pass." I'm pretty comfortable with static
       | types being a model where the compiler throws the type
       | information away at compile time, so I can type with abandon
       | without worrying that I'm going to impact runtime performance in
       | my generated code.
       | 
       | ... TypeScript may be one exception to my rule of thumb. It's
       | already compiling down to JavaScript, so you're already paying a
       | performance tax in having your code run in an interpreted
       | language. The question should be "how much more of a performance
       | tax are you paying annotating things with type runtime metadata?"
        
         | mrkeen wrote:
         | Why not just let the compiler build the deserialiser out of the
         | runtime information during compile time, and then throw out all
         | the types?
        
       | thanksgiving wrote:
       | I'm not an expert or anything but I sincerely feel like this is
       | barking up the wrong tree.
       | 
       | To anyone who works on typescript/JavaScript outside the context
       | of a web browser, you DO NOT matter as far as I am concerned. If
       | I was in control of JavaScript/typescript, I'd give zero
       | attention to your demands. You are not my top priority. Go away.
       | 
       | I feel like we are losing focus here. Should JavaScript be an all
       | purpose language? Yes. Should we focus on making a web browser
       | context, also yes.
       | 
       | The reason I led with this is that I think it is up to the
       | JavaScript folks and the web browser vendors to include support
       | for this before typescript can change what it emits. Ideally, I'd
       | say wait a couple of years after browser vendors include support
       | to implement this in typescript.
        
         | Eric_WVGG wrote:
         | I don't understand this comment at all. I mostly work on
         | client-side front-end and am absolutely desperate for better
         | type generation. Zod is making me want to jump out a window
         | right now.
        
           | colinmcd wrote:
           | Something like typebox (that's designed to map one-to-one
           | onto JSON Schema) may be better if codegen is a priority.
        
       | klysm wrote:
       | So this is asking for a completely different language?
        
       | DanRosenwasser wrote:
       | Hey all, TypeScript PM here.
       | 
       | I understand the desire here. Runtime type checking is often
       | necessary for data validation, and we can see lots of libraries
       | developed to help fill the gap here. But I think the fact that
       | there are so many libraries with different design decisions is
       | pretty indicative that this is not a solved problem with an
       | obvious solution. We knew this going into the early design of
       | TypeScript, and it's a principle that's held up very well.
       | 
       | What have been happy to find is that we've grown TypeScript to be
       | powerful enough to communicate precisely what runtime type-
       | checking libraries are actually doing, so that we can derive the
       | types directly. The dual of this is that people have the tools
       | they need to build up runtime type validation logic out of types
       | by using our APIs. That feels like a reasonable level of
       | flexibility.
        
         | 0xb0565e487 wrote:
         | I'm relatively new to programming and had a question about
         | TypeScript's functionality. Is there any specific reason why
         | TypeScript doesn't allow for the creation of custom and
         | intricate data types? For example, I'm unable to define a
         | number type within a specific range, or a string that adheres
         | to a certain pattern (like a postal code).
         | 
         | I'm imagining a language where I could define a custom data
         | type with a regular function. For instance, I could have a
         | method that the compiler would use to verify the validity of
         | what I input, as shown below:
         | 
         | function PercentType(value: number) { if (value > 100 || value
         | < 0) throw new Error();                   return true;
         | 
         | }
         | 
         | Is the lack of such a feature in TypeScript (or any language) a
         | deliberate design decision to avoid unnecessary complexity, or
         | due to technical constraints such as performance
         | considerations?
        
           | bern4444 wrote:
           | Some of this is possible in the type system like a range:
           | From stackoverflow:
           | https://stackoverflow.com/questions/39494689/is-it-
           | possible-...                 type Enumerate<N extends number,
           | Acc extends number[] = []> = Acc['length'] extends N
           | ? Acc[number]         : Enumerate<N, [...Acc, Acc['length']]>
           | type NumberRange<F extends number, T extends number> =
           | Exclude<Enumerate<T>, Enumerate<F>>            type
           | ZeroToOneHundred = NumberRange<0, 100>
           | 
           | One limitation is that this has to be bounded on both ends so
           | constructing a type for something like GreaterThanZero is not
           | possible.
           | 
           | Similarly for zip codes you could create a union of all
           | possible zip codes like this:                 type USZipCodes
           | = '90210' | ...
           | 
           | Often with the idea you have in mind the solution is to
           | implement an object where the constructor does a run time
           | check of the requirements and if the checks pass instantiate
           | the instance and otherwise throw a run time error.
           | 
           | In functional programming this is often handled with the
           | Option which can be thought of an array with exactly 0 or 1
           | elements always. This [0] is a library I wrote for JS/TS that
           | provides an implementation of Options.
           | 
           | [0] https://github.com/sbernheim4/excoptional
        
         | scotty79 wrote:
         | Maybe official preprocessor plugins for TypeScript compiler
         | could help?
         | 
         | I understand that everybody who needs it can already put their
         | own preprocessor that generates runtime objects from type
         | information before the code is passed to tsc for compilation.
         | 
         | But the effort is inconsistent and distributed.
         | 
         | If TypeScript officially supported pluggable preprocessor and
         | plugin ecosystem for it some good solutions might get
         | discovered.
        
           | andix wrote:
           | What's next? An official TypeScript UI framework, will it be
           | Vue, React, Next.js or Svelte? An official TypeScript date
           | library?
        
       | MrResearcher wrote:
       | Typescript already kind of has it, it's just they got it
       | backwards.
       | 
       | One needs to define their types as a const, like this one:
       | 
       | ``` [{ type: Number, name: "field1" }, { type: String, name:
       | "field2" }] as const ```
       | 
       | and then use Typescript magic to convert it into the fully
       | fledged Typescript type.
       | 
       | Yes, it's annoying, but it's more flexible.
        
         | epolanski wrote:
         | This is as useful as vue 2's prop types: very little.
        
         | gloryjulio wrote:
         | U just need a library to enable this to reduce the boilerplate.
         | I don't see how that's impossible
        
         | asherah wrote:
         | this wouldn't work for interfaces, or more complicated types
        
       | noobdev9000 wrote:
       | Modern TS code is hard for me to read and reason, and I use Rust,
       | Haskell, C++. Way too complex for my brain
        
         | epolanski wrote:
         | Well, as always, this really depends on our the code we read
         | and our background.
         | 
         | TypeScript's type system is indeed way more verbose than
         | Haskell's.
        
       | BiteCode_dev wrote:
       | While type hints in Python are not as nice as typescript
       | annotations, I understand their plea, because once thing Python
       | does better is runtime introspection.
       | 
       | Which means we can have dataclass, pydantic, typer and fastapi
       | all generating their stuff from regular type hints. No need for
       | special syntax or functions. And that's super nice.
        
       | orthecreedence wrote:
       | Reflection on a language that compiles to javascript? I don't get
       | it. Why are people so allergic to macros?? Every stinking
       | language that has compiled to JS has had a chance to do macros
       | but they don't. Coming from many years spent in the lisp world,
       | metaprogramming is this incredible feature that, in my
       | experience, completely negates the need for runtime reflection
       | while adding many other possibilities as well. I get it...the
       | syntax is way harder when your language isn't basically a pre-
       | parsed AST. But rust does it.
       | 
       | Seems like a huge waste.
        
         | kazinator wrote:
         | TypeScript imposes more detailed typing onto JS objects. It
         | makes sense to ask the question: can the type imposed by TS
         | onto a JS object be reified as a run-time object?
         | 
         | It could be, but maybe the use cases for that can be solved in
         | another way that don't require the type system at run-time.
         | 
         | The good news is that static type is, well, static. All the
         | objects of the same type can share a pointer to the same type
         | metadata. The overhead is thus not necessarily huge: one extra
         | property to initialize when an object is created.
         | 
         | The generated TS code has to carry the run-time support
         | routines for the type stuff though.
        
         | tinideiznaimnou wrote:
         | Preach, brother!
         | 
         | If only ECMAScript had a native macro system, TypeScript could
         | be just a library - and I would have zero problems with its
         | existence.
         | 
         | You're probably familiar with the following anecdote:
         | 
         | >Eich originally joined intending to put Scheme "in the
         | browser",[4] but his Netscape superiors insisted that the
         | language's syntax resemble that of Java.
         | 
         | JavaScript became the language that we all love to hate due to
         | political, not technical reasons. Yet people look at me funny
         | when I call out the TS/React monoculture as the blatant
         | corporate power grab that it is.
        
           | kazinator wrote:
           | If ECMAScript had macros such that TypeScript could be a
           | library, people would still ask the question: can we attach
           | the type object manipulated by the library to the objects?
           | 
           | If ECMAScript had macros, page load times would skyrocket.
           | ECMAScript and/or browsers would have to define a way to
           | distinguish JS-with-macros files and embedded scripts from
           | plain old JS that doesn't require expansion.
           | 
           | Any JS code bases with complicated macros that take time and
           | memory to expand would have to be expanded by the developer
           | and shipped expanded. So, back to the same model as
           | TypeScript.
        
       | paulddraper wrote:
       | Better title: TypeScript please give us reflection/runtime types
       | 
       | AFAIK the best current solution is emitlDecoratorMetadata. [1]
       | 
       | [1]
       | https://www.typescriptlang.org/tsconfig#emitDecoratorMetadat...
        
         | dang wrote:
         | Ok, we'll add that bit to the title above. Thanks!
        
       | rcme wrote:
       | There are good typed deserialization libraries. Personally, I
       | prefer runtypes but there are options.
        
         | decs wrote:
         | Agree, zod, arktype, and typia are impressively easy to use!
         | Though, I feel that this environment created a different
         | problem of spreading developers into multiple solutions. So for
         | lib devs like me, we end up having to choose between coupling
         | with a single validation lib or managing support for multiple
         | (like tRPC does). To address that, I ended up doing a reusable
         | lightweight lib that wraps that logic for supporting multiple:
         | https://typeschema.com
        
       | andix wrote:
       | There is a good reason for not doing this. Typescript would
       | become some kind of runtime on top of JavaScript. A new language
       | that compiles to JavaScript. Currently TS is only JavaScript with
       | type annotations.
       | 
       | There are many languages that compile to JavaScript. Pick one of
       | them and use it!
       | 
       | And I have the feeling, that people who want runtime typed
       | Typescript would rather like to write Java/OOP style code instead
       | of JavaScript. But JavaScript is a dynamically typed language,
       | and that's also nice. Be happy with what you have!
        
         | shortrounddev2 wrote:
         | The reason we want typescript to have java-like features is
         | because we work on teams which have decided, outside of our
         | control, that we are going to write our backend in JS/TS simply
         | because it's easier for the bootcamp grads to transition to
         | backend since they already know JS on the frontend. If we wrote
         | our backend in Java, it would require the bootcamp grads to
         | learn a new language, which they're not prepared to do.
         | Choosing typescript or javascript on the backend is not about
         | choosing the right tool for the job (because they are
         | objectively not the right tool for backend development). It is
         | mostly about minimizing the cost of our labor sourcing
         | 
         | As such, RTTI would make typescript go from a compromise
         | language (we use it because we are stuck in the JS ecosystem,
         | not because it's a good tool) to a legitimately useful backend
         | language (We use it because it has the right features for the
         | use-case)
         | 
         | If I could choose what language we use on the backend, I would
         | leave the JS ecosystem entirely and write everything in .Net.
         | But it's difficult to find .Net developers since every bootcamp
         | these days produces react developers
        
           | andix wrote:
           | So you want TypeScript to be Java, but you don't want to use
           | Java, because your developers only know TypeScipt. Sounds
           | like an unsolvable problem.
        
             | shortrounddev2 wrote:
             | The solution is to pile features onto typescript that were
             | never intended to be there, the same way we have done with
             | Javascript, HTML, CSS, and HTTP. None of these things are
             | pure, and all of them have been ravaged by competing
             | interests and committees who were all making financially
             | driven decisions when writing the standards.
             | 
             | It would be best to abandon javascript and typescript as
             | backend languages. I don't mind them on the frontend
             | because a typescript compiler is useful when the
             | application you're writing is _originating_ data and not
             | _receiving_ it. And javascript does DOM manipulation
             | admirably. However, we live in a world where humans are
             | more expensive than computers. If I had my way, we 'd write
             | C# backends and Angular/Typescript frontends, but that's
             | not the world we live in. Everything has to be JS because
             | most people employed as "software engineers" are not truly
             | very good at their jobs
        
         | twelve40 wrote:
         | > dynamically typed language, and that's also nice
         | 
         | what is nice about a dynamically typed language? like,
         | seriously, can you list some reasons for a dynamically typed
         | language to exist?
        
           | andix wrote:
           | Types can get complicated. To an extent that code gets
           | extremely overcomplicated by using a typed language. Typed
           | languages encourage to use many layers of DTOs and models.
           | 
           | With dynamic languages you can just code, without being held
           | back by a OOP type system, but it can get very complicated to
           | understand what the typed of you parameters and return values
           | actually are.
           | 
           | Typescript gives you the best of both worlds.
        
           | Barrin92 wrote:
           | Peter Norvig summed it up in one of his presentations
           | https://norvig.com/design-patterns/design-patterns.pdf
           | 
           |  _" Dynamic Languages have fewer language limitations Less
           | need for bookkeeping objects and classes Less need to get
           | around class-restricted design. Study of the Design Patterns
           | book: 16 of 23 patterns have qualitatively simpler
           | implementation in Lisp or Dylan than in C++ for at least some
           | uses of each pattern[...]"_
        
         | fzeindl wrote:
         | You can use Kotlin and compile to JavaScript. It's type system
         | is better and the standard library greater _scnr_
        
           | andix wrote:
           | Exactly. Pick whatever language you like. There are many more
           | options.
        
           | booi wrote:
           | This is the way. Best of both worlds although using js
           | libraries is kind of a pain
        
         | adjav wrote:
         | My understanding has long been that web developers have decided
         | to turn Javascript into C#, one proposal at a time.
        
         | asherah wrote:
         | typescript enums exist and are compiled to javascript (other
         | than const enums)
        
           | lolinder wrote:
           | That TypeScript fails to live up perfectly to its design
           | goals doesn't mean it should adopt additional anti-features.
        
           | andix wrote:
           | And that's one of the features of typescript that really
           | sucks. String union types work so much better.
        
             | moystard wrote:
             | Why does typescript enum really suck?
        
               | Waterluvian wrote:
               | There's a _ton_ written about this if you search for
               | "typescript enums."
               | 
               | I just finished removing them from a major project.
               | Here's a few of my personal notes:
               | 
               | 1. They don't play well with duck typing. (Eg. show me a
               | subset of an enum)
               | 
               | 2. They require an import every time you want to utilize
               | them.
               | 
               | 3. They are pretty wordy compared to union strings.
               | 
               | 4. Their string value version encourages misuse as a key-
               | value pair.
               | 
               | 5. Unless you use the string value version, they suck to
               | debug because logs just show 0,1,2,3.
        
               | WorldMaker wrote:
               | Another one: they conflate Type and (Locator) Instance in
               | a unique way that just about nothing else in TS does.
               | Those are two very different things with the same name
               | with Typescript's (antiquated) enums.
               | 
               | There are too many ways to accidentally
               | import/redeclare/rescope the Type of an enum so that TS
               | "knows" the Type, but because that type (generally) has
               | the same "name" as the most likely (Locator) Instance it
               | assumes the same access applies leaving runtime errors
               | behind when that Instance isn't actually
               | imported/available. Typescript has no easy way to tell
               | the difference between access to the Type isn't access to
               | the (Locator) Instance (nor vice versa). Reasoning about
               | those runtime errors or preventing them is additionally
               | tough for people _too_ because of the same  "name"
               | problem for two different things.
               | 
               | This is something that's painfully hard to avoid in cases
               | where you are trying to encapsulate an API's Types
               | separate from its imports/exports because they might be
               | introduced or manipulated at runtime (plugins, sandboxes,
               | proxies, etc). Unfortunately, this is also too easy to
               | accidentally do even when you aren't intentionally doing
               | something complicated like that (trying to generate
               | automated .d.ts files in a bundling toolchain, for
               | example, when APIs are in the boundary space between
               | unintentional public API and internal tree-shaking or
               | optimized symbol renaming).
        
               | andix wrote:
               | Thanks for putting into words why they just "feel" so
               | wrong.
        
               | graypegg wrote:
               | Typescript enums emit a really weird object at runtime
               | 
               | enum CheckboxState { On; ParentOn; Off; }
               | 
               | Becomes
               | 
               | { [0]: "On", "On": 0, [1]: "ParentOn", "ParentOn": 1,
               | [2]: "Off", "Off": 2 }
               | 
               | So things like Object.keys give bizarre results. It's
               | done this way so you can use the name or the value as an
               | index.
        
               | andix wrote:
               | Let's turn it around, union types are so much easier to
               | use and so much more powerful. Enums have only a small
               | subset of the features, are not compatible to JavaScript
               | code and are hard to understand (read the docs about type
               | script enums and you will see).
        
         | joshmarinacci wrote:
         | Dynamically typed means I can look up the type of an object at
         | runtime. JavaScript lets us do this with classes/prototypes.
         | Since TypeScript adds types and enums and interfaces, why can't
         | it let us look up those at runtime too? It would seem to fit
         | with the way JS works.
        
         | Arainach wrote:
         | For any sufficiently large project, dynamically typed languages
         | are not nice, they are a series of interweaved disasters
         | consisting of constantly tripping over what you thought was
         | your feet but is actually NaN and trying to fall forward rather
         | than collapsing entirely.
         | 
         | In every workflow I've seen, TypeScript is already a language
         | that gets compiled to JS. It might as well take full advantage.
        
           | andix wrote:
           | Typescript is not really compiled to JS, only the type
           | annotations are removed.
           | 
           | To make dynamically typed languages nice for bigger projects
           | we have typescript for type annotations (it's does not make
           | it a statically typed language!). It is there, it is used, it
           | scales and it works. If your type script project "collapses
           | entirely", it's probably not the languages fault.
        
         | vbezhenar wrote:
         | TypeScript is not JavaScript with type annotations. One of the
         | modes, may be. TypeScript supports emitting old JS
         | constructions which look nothing like original typescript code.
         | So it's more like type annotations + babel. Adding one more
         | thing to this set. Extremely useful thing. I think it's a good
         | idea. I miss it. It's crazy that I can't JSON.parse string into
         | typed structure safely. Every other language can do that.
        
           | andix wrote:
           | No, this is not crazy, this is the design goal of TypeScript.
           | 
           | If you want a statically and runtime typed language in the
           | browser, you should not use typescript. Use Kotlin, Rescript,
           | ...
        
         | whalesalad wrote:
         | You can have both. This is the beauty of lisp -
         | https://en.wikipedia.org/wiki/Homoiconicity
         | 
         | But in all seriousness you can gain reflection without a
         | runtime. Just expose the types as data.
         | 
         | Seems foolish to _not_ do something like this. Particularly
         | when you look at the lengths so many devs have gone to try and
         | replicate it.
        
         | armchairhacker wrote:
         | Not necessarily.
         | 
         | Consider                   const fooType =
         | generateTypeInfo!<Foo>();
         | 
         | Where `generateTypeInfo!` is a macro that expands to a JS
         | object encoding the "Foo" type (e.g. if `Foo` a record type,
         | `fooType` will be a record with its encoded field types). It
         | still compiles to readable JavaScript.
         | 
         | What this macro _does_ break is that TS = JavaScript with type
         | annotations, and all you need to do to compile is remove those
         | annotations (excluding enums, but they are all-but-deprecated
         | and obsoleted by string unions) without even type-checking.
         | Since now you also need to expand the `generateTypeInfo!`,
         | which requires actually computing the structural type of `Foo`
         | (and if you want any sort of nominal type metadata, that too).
         | 
         | And that's still a problem because type-checking is slow, but
         | removing the annotations is fast. If there's a limited way to
         | resolve types for these annotations which restricts the
         | resolution scope, that would be a good candidate.
         | 
         | ---
         | 
         | There's also more issues with TypeScript having a separate
         | runtime besides it not being JavaScript. TypeScript types are
         | structural, so they currently _are_ available at runtime: to
         | determine an object's type, inspect its structure. Anything
         | more and you quickly run into non-trivial cases being literally
         | impossible: if you want erased nominal information like whether
         | a string is part of a string union, TypeScript's type system is
         | Turing complete; and if you want the nominal type name,
         | implicit structural conversions mean that once a value leaves
         | its annotated cast or definition it's effectively undefined.
        
           | baxuz wrote:
           | I'm not sure what `generateTypeInfo!<Foo>();` is supposed to
           | be, but as far as I'm aware there is no way to execute
           | anything in a JS engine from a type generic.
        
             | __jem wrote:
             | It's meant to represent a hypothetical TS compiler builtin
             | that does codegen for you.
        
           | scotty79 wrote:
           | What if this macro could be expanded into valid typescript by
           | preprocessor that runs before typescript compiler?
        
         | random_mutex wrote:
         | ReasonML
        
           | dimitrios1 wrote:
           | ReasonML suffered tremendously when the community split and
           | went off to do ReScript. I don't think either have recovered
           | the original luster.
        
           | [deleted]
        
         | paxys wrote:
         | > Typescript would become some kind of runtime on top of
         | JavaScript. A new language that compiles to JavaScript.
         | 
         | Typescript isn't a runtime on top of JavaScript, but it very
         | much is "a new language that compiles to JavaScript".
        
           | codeflo wrote:
           | Yes, but it's special in that it compiles by erasure. (Mostly
           | -- the cases where it doesn't are considered historical
           | mistakes.)
        
             | vanviegen wrote:
             | Ok. And why is that important? So one could meticulously
             | add typing to your JavaScript application, and then strip
             | it all out with a regex in order to deploy? What real-life
             | benefit does 'compile by erasure' offer?
        
               | codeflo wrote:
               | That's essentially what all the fast transpilers do --
               | esbuild, babel etc. They don't typecheck the code at all,
               | they just parse it and strip out the type annotations.
               | 
               | This means that you can run and test code that doesn't
               | typecheck yet -- very useful during development. Also,
               | production builds can run the typechecker in parallel
               | with other build steps. That's how we optimized our build
               | system at work.
               | 
               | But essentially, it's a design choice by the TypeScript
               | team. They've decided that TypeScript shouldn't add
               | runtime features on top of JavaScript, but instead should
               | simply _type_ JavaScript, and I think this has
               | contributed to the success of TypeScript.
        
               | johnfn wrote:
               | I think the point is that TypeScript isn't really "a new
               | language". It's an existing language with an additional
               | feature. The fact that TS maintains the promise that
               | types can be stripped means that TS is upholding a
               | contract that it will not stray from Javascript. It
               | can't, for example, add new language features.
        
         | a_wild_dandan wrote:
         | I'm misunderstanding your reasoning here. TS is _already_ a new
         | (superset) language which compiles to JS. Runtime types solve
         | the problem of maintaining two duplicate, separate type systems
         | -- one for compilation, one for validating data. I 'm not
         | seeing how that's related to OOP. For instance, my preferred
         | workaround lib for this problem, `io-ts`, leans hard on
         | functional programming.
        
           | PaulHoule wrote:
           | Yeah but just because types exist at compile time doesn't
           | mean they exist at run time.
           | 
           | For instance with Generics in Java a List<String> is the same
           | as a List<Integer> at run time, the compiler can enforce
           | rules that let you add a String to one but not add a String
           | to the other but the runtime has no idea. This has various
           | negative consequences but it also let them retrofit generic
           | collections on top of the old collection implementation in
           | Java unlike the disaster in .NET where both had to coexist
           | for years.
           | 
           | Similarly C compiles to machine code and in machine code
           | there are just memory locations and registers, types are
           | implicit in how you use those things but not spelled out
           | explicitly. C++ does have RTTI but is one of the many open
           | pits, like Exceptions, in C++.
           | 
           | Typescript types are the same way, once the compiler has done
           | its type checking you know the code is going to behave
           | according to the type system even if the types have been
           | "erased".
        
             | einrealist wrote:
             | Java's generic type information (aka parameterized types)
             | is available at runtime through reflection.
        
           | Quekid5 wrote:
           | > TS is already a new (superset) language which compiles to
           | JS.
           | 
           | Yes, but also no. The point is that it doesn't really add
           | more _semantics_ over plain JS. You can erase all the type
           | annotations (well, replace with  "any") and it'll do the same
           | thing if you ignore the type checking.
           | 
           | If there's any "transformation" from TS to different-looking
           | JS[0], it's mostly just syntactic embellishment (think LISP
           | macros and such), not semantic.
           | 
           | [0] Not sure if there is, tbh
        
           | lolinder wrote:
           | Emitting types would run counter to several of TypeScript's
           | Design Goals [0]. In particular, it would violate:
           | 
           | > 3. Impose no runtime overhead on emitted programs.
           | 
           | > 9. Use a consistent, fully erasable, structural type
           | system.
           | 
           | It's also explicitly called out as a non-goal in that doc:
           | 
           | > 5. Add or rely on run-time type information in programs, or
           | emit different code based on the results of the type system.
           | Instead, encourage programming patterns that do not require
           | run-time metadata.
           | 
           | > 6. Provide additional runtime functionality or libraries.
           | Instead, use TypeScript to describe existing libraries.
           | 
           | [0] https://github.com/Microsoft/TypeScript/wiki/TypeScript-
           | Desi...
        
             | wvenable wrote:
             | Emitting type information to some kind of separate common
             | format wouldn't undermine most of these goals. There are
             | already apparently dozens of tools that people can use for
             | this task in a dozen different ways. Just have TS emit the
             | data.
             | 
             | In this way there is no runtime overhead (point #3) -- it's
             | pure data to be optionally consumed. They wouldn't have to
             | prescribe any particular reflection module (point #6).
             | 
             | The only point this would violate is #5 but that is, of
             | course, the point. We want them to reconsider because it's
             | such a powerfully useful feature. And people are doing it
             | on top of and outside of TypeScript where TypeScript would
             | be best tool to give this information for TS users.
        
               | lolinder wrote:
               | It would violate #9 no matter what implementation you
               | use, and #9 is absolutely essential to tools like
               | ESBuild, Deno, and Bun, which all rely on the assumption
               | that they can nearly instantly strip out the type
               | annotations and execute the code as JS.
        
               | wvenable wrote:
               | You'd still be able to execute the code as JS. Your
               | library that uses type reflection wouldn't work anymore
               | but that's true of all these solutions.
        
               | lolinder wrote:
               | No, it's not. I can take a library that uses zod, strip
               | the annotations, and it will execute perfectly anywhere I
               | want to run it.
        
               | wvenable wrote:
               | That's because Zod doesn't use type annotations as input.
               | We're talking about wanting to use type annotations as
               | input so that we don't have to make our code as ugly as
               | Zod makes it.
        
               | RyanCavanaugh wrote:
               | TypeScript dev lead here.
               | 
               | Today, you can already write a program using the
               | TypeScript API to inspect all of this information to
               | accomplish whichever scenario is at hand. The existence
               | of all these tools, each with different opinions on
               | design direction and implementation trade-offs,
               | demonstrate that this is possible. Yet it's not
               | demonstrated how those tools would benefit from reading
               | from a data file as opposed to using the TypeScript API.
               | 
               | Something like api-extractor has different constraints
               | from io-ts which has different constraints from
               | typescript-schema. The API exists today and is apparently
               | sufficient to meet all these tools' needs, yet what's
               | proposed is a data file that can encapsulate any possible
               | operation you might perform with that API.
               | 
               | Having TypeScript try to imagine, implement, and maintain
               | a data file format that can satisfy _all_ of these tools'
               | use cases, plus any future tool's use case, is a
               | tremendous effort with no clear upside over using the
               | existing API.
               | 
               | It's easy to gain support for the idea of "put the types
               | in a file" because anyone reading that can imagine a
               | straightforward implementation that just achieves their
               | particular goals, but everyone's goals are different and
               | the amount of overlap is not actually all that high when
               | you look at the wide breadth of tools in the list.
               | There's a very different amount of information you'd need
               | to enable a type-aware linter (which is basically type
               | info on every expression in the program!), as compared to
               | a simple documentation generator (which might only need
               | top-level declaration info).
        
               | wvenable wrote:
               | Hard to argue with that! I wonder why this isn't
               | considered a solution to the author of this site.
        
               | lolinder wrote:
               | The author explicitly calls out the existence of all of
               | these tools as _being_ the problem. They seem to want a
               | canonical version that TypeScript itself officially
               | supports.
        
               | RyanCavanaugh wrote:
               | Situation: There are 14 competing type representation
               | formats
               | 
               | TypeScript: We can write our own type file format!
               | 
               | Situation: There are 15 competing type representation
               | formats
        
             | vbezhenar wrote:
             | If some design goals are stupid they must be changed. It's
             | not god word. It's human word. And this human was wrong.
        
               | lolinder wrote:
               | This isn't just some of the design goals, type erasure is
               | the fundamental guiding principle of TypeScript that
               | allowed it to become successful. Throwing it away because
               | it complicates certain programming patterns should not be
               | done lightly.
        
               | andix wrote:
               | In my opinion they are not stupid, they make a lot of
               | sense. And many people seem to agree and use typescript.
               | If you don't agree, use something else. There are many
               | alternatives, pick something that is ,,smart" in your
               | opinion.
        
           | epolanski wrote:
           | Small ot, if you like io-ts, @effect/schema is the spiritual
           | successor from the same ecosystem.
           | 
           | The fp-ts, io-ts author Giulio Canti has decided that the
           | best way to proceed was to merge the two communities of fp-ts
           | and effect, even though it's his work that is mostly known
           | with effect being much more niche.
           | 
           | It's essentially io-ts squared in its powers. The amount of
           | things you get from schemas are endless, not just decoders
           | and encoders but also apis, lenses, constructors, and many
           | other things.
           | 
           | It's obviously heavily fp-leaning, albeit I would say effect
           | systems such as effect, based on Scala's ZIO, are much more
           | approachable than haskell/purescript-like languages.
        
       | benatkin wrote:
       | Please give me an integer type.
       | 
       | Just kidding, but I'm not pushing for JavaScript anymore. If
       | rust/WASM works as well, I say go with that.
        
         | orthecreedence wrote:
         | > Just kidding, but I'm not pushing for JavaScript anymore. If
         | rust/WASM works as well, I say go with that.
         | 
         | This is the direction I'm going. I use rust a LOT these days,
         | and after trying out typescript for a few projects over the
         | course of a few months, I absolutely hate it when comparing it
         | to rust or even any other compiled language. Something about it
         | just rubs me the wrong way...maybe the fact that it has to be
         | JS-compatible, so the type system seems really crude? I can't
         | put my finger on it. It sort of hits this weird inbetween where
         | js is quick to write/prototype and rust is extremely "correct"
         | but the ts type system slows things down/annoys me without
         | providing enough benefits. Also, working with it in nested
         | projects via npm tends to be a huge pain in the ass. Maybe I
         | need to switch to Deno or something so it's native?
         | 
         | Now that rust/wasm is ramping up, I'm considering using rust
         | for the API layer and something like Leptos for the frontend.
         | I've been getting into svelte a lot, and Leptos seems similar
         | enough to not be a huge leap.
        
           | benatkin wrote:
           | I'm curious what you think about Dart/Flutter if you've
           | gotten a chance to dig into them. I am very impressed, but it
           | trades simplicity for being cross platform. It has a nice set
           | of types though. https://dart.dev/language/built-in-types
           | 
           | It also has a WASM compilation target that is under early
           | development. https://docs.flutter.dev/platform-
           | integration/web/wasm
        
       | dvt wrote:
       | I dunno, this seems like it would be out of scope for something
       | like TS proper (there's already been a ton of feature creep,
       | anyway). Reflection is generally implemented via runtimes, which
       | can be very clunky. Is it really worth doing this in JS? Tooling
       | already seems too bulky as is.
        
       | kazinator wrote:
       | * * *
        
       | Waterluvian wrote:
       | Above all of what I write below, I think that this is a very
       | valid issue for discussion and debate. I don't think there's one
       | objective correct answer. So this isn't me dictating what
       | TypeScript shalt be.
       | 
       | TypeScript is an entirely optional layer on top of JavaScript,
       | with the exception of `Enum` that emits an object, many of which
       | see as a mistake. You don't even have to "transform" TS code to
       | get JS: you just have to delete the typings. The rest is just JS.
       | 
       | Unless you want to depart heavily from this principle, which I
       | think is crucially important so long as there's still human-
       | maintained JS code out there, what I think they're asking for is
       | a library to take the types and write serializers/validators for.
       | Which there are many of. So I think the real request is to make
       | the one canonical choice out of a field of many choices, which I
       | also agree with the spirit of. But is that really in scope for
       | the TypeScript project?
       | 
       | What I personally don't want is runtime reflection for
       | TypeScript. Or at least, not a core part of the language. Because
       | I like that TypeScript is just for compilation and at runtime you
       | just have JavaScript, without any sort of abstraction layer that
       | turns JS into some sort of intermediate representation with
       | overhead or more indirection for debugging. I _cherish_ the fact
       | that even without source mappings, my output JS is perfectly
       | legible and debuggable.
        
       | zoogeny wrote:
       | This is the perennial problem with language design. Everyone
       | wants something and they even have reasonable grounds to ask for
       | it.
       | 
       | I myself have come across moments where some rtti/reflection
       | would have been useful. In those cases I was often able to use
       | branded types. Having something like that built into the language
       | might be nice. But I'd also support TypeScript and JavaScript
       | developers making the choice not to support it ever. IME, working
       | around rtti type code often forces the programmer to come up with
       | a much better approach.
        
       | JohnDotAwesome wrote:
       | Instead of pleading to the TypeScript gods, why not make a deal
       | with the JavaScript deities? Let's get type-checking into JS!
       | 
       | If I want runtime types, I reach for type guards. If I need to do
       | this a lot, then I reach for io-ts or zod and pretty much
       | exclusively write types as validators/codecs/schemas or what-
       | have-you.
       | 
       | I don't think TS - as a specification, type-checker, community,
       | or otherwise - needs to concern itself with runtime validation
       | UNLESS JS gets some agreed upon way to do it.
        
         | epolanski wrote:
         | I have huge doubts whether I'd like JS to have it really.
         | 
         | I feel like Promises, decorators and the new pipe operator all
         | went into the wrong directions with their implementations.
        
       | frou_dh wrote:
       | I was perfectly satisfied with using https://zod.dev for some
       | runtime data validation, and found it really cool that I didn't
       | have to define some nominal type off to the side and could
       | instead just say what I meant inline using a fluent API.
        
         | epolanski wrote:
         | Nominal types have different use cases.
         | 
         | Simple one, distinguishing `2` number with `2` currency in your
         | domain.
        
       | unilynx wrote:
       | What I really miss is a way to extract type information directly
       | from the compiler. Ie to have a 'macro' that takes a type as an
       | input and returns a readable text or a json structure describing
       | that type
       | 
       | It still wouldn't be runtime typing, the compiler would just
       | insert a constant wherever you use such a macro so I don't think
       | it would conflict with typescript's goals. But it would make it a
       | lot easier to build extra checks when deserializing data - or
       | just give richer debug information.
       | 
       | But it would probably be hard for eg esbuild to support such a
       | feature if tsc adds it. So I couldn't use it anyway :)
        
       | william-evans wrote:
       | As listed, Typebox is excellent for this use case - it correctly
       | implements the JSON schema spec and allows you to use it to write
       | OpenAPI specifications. At my company we use this to write type-
       | safe handlers & generate our API documentation!
        
       | paxys wrote:
       | I had to read their problem statement like 4 times to understand
       | what they are asking for. This is exactly why simple, concise
       | writing is essential, and their wall of text is...not it.
       | 
       | > I love you. You do amazing work. You are gods among mortals.
       | You have brought JavaScript from the darkness, and given it the
       | warm light of strong typing. Look upon us, the cowering meek
       | masses, and understand that we live in the muck and mire of a
       | world where we are doomed to endlessly crawl on our bellies
       | through the alleys of Github seeking the one true npm. We will
       | forever wonder in the dark, until we have a type reflection
       | model.
       | 
       | What even are they getting at?
       | 
       | As for their actual ask (runtime type safety), it is probably not
       | going to happen because the TypeScript project has drawn the line
       | at not being an alternate or additive runtime for JavaScript.
       | Their job is simply to compile down to JS code and exit the
       | picture. Whatever happens after that (in V8 or elsewhere) is your
       | own business.
       | 
       | > TypeScript Needs to Emit Runtime Type Information
       | 
       | This is not possible at all because TypeScript is a _compiler_.
       | They are really asking for a net new product which has very
       | little to do with the TypeScript that exists today.
        
         | [deleted]
        
         | tinideiznaimnou wrote:
         | >What even are they getting at?
         | 
         | They are literally _groveling_. That should tell you enough.
         | Cringe af
         | 
         | Just consider how far we've progressed since the 90s - instead
         | of helplessly cursing at a distant Microsoft, we now have the
         | freedom to _beg_ them to add missing nutrients to the shit that
         | they 're forcing down our throats! And a realistic chance of
         | them hearing us, maybe even responding: "hell, are you really
         | stupid enough to think that we're feeding you shit because of
         | its _nutritional value_? "
         | 
         | Embrace, extend, extinguish is part of Microsoft's "corporate
         | DNA". TypeScript amounts to a wildly successful social
         | engineering attack against the open Web development community.
         | Its purpose is to _subjugate_ , plain and simple. It's "open
         | source" just as much as it's a "superset of JavaScript" - which
         | is to say, it isn't either, but pretends to be. And nobody bats
         | an eyelid.
         | 
         | There was a phase (2010s) when JS-to-JS compilation was not
         | only a valid approach, but a necessity. Rolling out ES6 to the
         | heterogeneous browser landscape was a _tremendous_ feat in
         | distributed, decentralized, _public_ computing. It was also
         | Microsoft 's opening: "JS transpilers are becoming a jungle;
         | now is the time to make the One True Transpiler which
         | accidentally only kills JS!" So they did, to great success: how
         | many newbies you see learning how JS works, before making an
         | informed decision whether they need TS or not? [1]
         | 
         | But while rookie programmers with oodles of talent and ability
         | to learn (and the occasional stroke of beginner's luck at
         | achieving the impossible) are busy enthusiastically developing
         | learned helplessness from wrangling the TypeScript toolchain
         | (and the other cancerous growths on the Web ecosystem, take
         | your pick), they can't empower themselves. If they can't
         | empower themselves, they can't innovate. If they can't
         | innovate, they have zero chance of competing with the lumbering
         | shoggoth that is the surveillance economy. If nobody takes down
         | the surveillance economy this generation, bad times for
         | everyone. Am I explaining this simply enough?
         | 
         | We've even got the TS lead in this comment section (but of
         | course)! And he's probably ready to question my mental
         | faculties (in friendly, polite American) for relating my lived
         | experience (and the only remotely sane explanation for it that
         | I can conjure). Because his salary depends on that or smth.
         | Hello Ryan, have you folks fixed TS generating _invalid_ ESM
         | imports and telling people to suck it up in the
         | documentation[0] yet? (Shhh, let 's see if he responds.)
         | 
         | And how do you feel about people "ironically" humiliating
         | themselves to _beg_ you for stuff (unrealistic or not) -
         | considering the author of that README addresses you and your
         | team as something akin to demigods?
         | 
         | [0]: https://www.typescriptlang.org/docs/handbook/esm-node.html
         | "This might feel a bit cumbersome at first, but TypeScript
         | tooling like auto-imports and path completion will typically
         | just do this for you." Yeah, no.
         | 
         | [1]: https://news.ycombinator.com/item?id=36638068 proves I'm
         | not alone in this sentiment. Thanks HN, for once!
        
         | jxf wrote:
         | > This is exactly why simple, concise writing is essential, and
         | their wall of text is...not it.
         | 
         | I only skimmed this but felt it was clear. They're asking for
         | TypeScript, as part of the type erasure, to emit the type
         | information it has discovered about the types in a side channel
         | to the emitted JavaScript. Think of, say, PDB files as an
         | analogy.
         | 
         | > They are really asking for a net new product which has very
         | little to do with the TypeScript that exists today.
         | 
         | This is information TypeScript already has today but which it
         | discards. It wouldn't take a "net new product".
        
           | [deleted]
        
           | RyanCavanaugh wrote:
           | You can use the TypeScript API to generate this information
           | at whichever level of detail you want.
           | 
           | The level of detail TS has about types during the checking
           | phase is much higher than you would want in practice for 99%
           | of projects (e.g. 1 + 2 + 3 has 6 different types associated
           | with it).
           | 
           | The level of detail TS has about types during the checking
           | phase is potentially lower than you would want in practice
           | for a lot of projects (which is critical since that makes the
           | whole feature useless if that happens). For example, the list
           | of properties of a particular generic instantiation is lazily
           | computed, but it's possible your program never pulls on the
           | list so it never exists in the first place, yet is something
           | your type-based tool might want to know.
        
         | spatialaustin wrote:
         | At the top of the README they link to "The 7 year old Github
         | issue" which describes the problem in they way you're asking
         | for.
         | 
         | https://github.com/microsoft/TypeScript/issues/3628
        
         | jauntywundrkind wrote:
         | Counter to this post, as soon as I read the title I knew what
         | this was, & I knew it was speaking exactly to something we've
         | wanted for a long time. This is asking for more official &
         | better supported https://github.com/rbuckton/reflect-metadata .
         | 
         | TypeScript _is_ a compiler. It has a lot of type information
         | during compilation. We could write that type information out
         | into a file. Instead what we do is throw that information out
         | when the compile ends. Taking all that typing information  &
         | throwing it away at the end of compile time is a bad dumb &
         | silly limitation. Especially for a language like JavaScript,
         | which historically could be semi-proud it had such a strong
         | _Everything Is An Object_ philosophy running through it (such
         | as the malleable prototype-based inheritance system); so much
         | type information should be on that Class object. Reflect-
         | metadata for example defined new methods on Reflect to store
         | this metadata.
         | 
         | I could not be more delighted to see the pennon of this website
         | go up. We needed a rallying point for this. We needed a
         | rallying point for keeping class data around. A rallying point
         | for enriching the runtime with good actionable data is a good
         | rallying point.
         | 
         | It's not what's afoot here, but I think you're a bit off-base
         | about the impossibility of adding even _some_ type-safety. We
         | might not be able to get exact TS type safety. But we can
         | definitely build some safety in. Owing to the malleable
         | prototype-based type system in JS, we can add getters /setters
         | to objects to do a lot of type checking. This doesn't even
         | begin to explore the possibility of what we might do with
         | es2015's proxies, which could allow even more interesting
         | checks to be layered in. I also wish JS had an official AST
         | (and renderer), so had more official options for code-rewriting
         | that might let us weave in type checks.
         | 
         | What we can do as programmers is limited by what we have at our
         | disposal. Not throwing out all the typing information, keeping
         | it around at runtime, opens a lot of interesting doors.
        
         | johnfn wrote:
         | The first 7 words of the document are "TypeScript Needs to Emit
         | Runtime Type Information". Was that not clear? I guess I'm a
         | bit confused by your confusion, unless you don't know what
         | runtime type information is, in which case I suppose it's more
         | understandable - but in that case I suppose you're not really
         | the target audience.
        
         | cornstalks wrote:
         | > > _TypeScript Needs to Emit Runtime Type Information_
         | 
         | > _This is not possible at all because TypeScript is a_
         | compiler.
         | 
         | Sorry, can you clarify? Many compilers exist in other languages
         | that support RTTI, so it's not clear to me what you mean by
         | this.
        
           | paxys wrote:
           | There are plenty of projects that do the same for TypeScript
           | (e.g. https://github.com/typescript-rtti/typescript-rtti) and
           | plenty that support some kind of runtime reflection, and
           | overall emitting TS type information at compile time in some
           | readable format is a pretty trivial problem to solve. The
           | complicated part is on the other side - what do you do with
           | this information? How do you get JavaScript engines to
           | understand it?
        
             | wvenable wrote:
             | You don't get JavaScript engines to understand it. You use
             | a library.
        
               | paxys wrote:
               | So then...exactly how it is done today? TypeScript
               | provides an API to get type info at compile time.
               | Libraries write plugins to consume this type info and use
               | it for runtime and custom validation. What changes in
               | this new world?
        
         | goodpaul6 wrote:
         | > This is not possible at all because TypeScript is a compiler
         | 
         | Not sure if this supports your point. Lots of compilers emit
         | runtime type information, e.g. Golang, Java.
        
           | jakelazaroff wrote:
           | Those languages all have runtimes that can consume the type
           | information. TypeScript does not.
        
         | tylerhou wrote:
         | Nit: they're not asking for runtime type safety; they're asking
         | for the ability to reflect on types (at compile time, to
         | generate values) so that they can use type information at
         | runtime.
         | 
         | This helps ensure runtime type safety because (for example) it
         | would be great to have a generic "validation" function that
         | takes an arbitrary interface and an arbitrary object and
         | validates that object. One way to implement this would be to
         | use /compile time/ reflection to generate code (TS code
         | hypothetical, because I write C++ nowadays):
         | function validate<T>(obj: Any): T | null {         switch
         | constexpr (T) {         case String:           return typeof
         | obj == "string" ? obj : null;         case Array<U>
         | if (!Array.isArray(obj)) {             return null;           }
         | for (const u of obj) {             if (validate<U>(u) == null)
         | {               return null;             }           }
         | return obj;         // ... more base cases         }
         | for (const prop: (keyof T) of Reflect<T>.Properties()) {
         | if (validate<T[prop]>(obj[prop]) == null) {             return
         | null;           }         }              return obj;       }
         | interface Date {         year: String;         month: String;
         | day: String;       }
         | 
         | It would be great if this could generate /JavaScript/ code:
         | function validate__String__(obj) {         return typeof obj ==
         | "string" ? obj : null;       }             function
         | validate__Array$Date$__(obj) {         if (!Array.isArray(obj))
         | {           return null;         }         for (const u of obj)
         | {           if (validate__Date__(u) == null) {
         | return null;           }         }         return obj;       }
         | function validate__Date__(obj) {         for (const prop of
         | ["year", "month", "day"])) {           if
         | (validate__String__(obj[prop]) == null) {             return
         | null;           }         }         return obj;       }
         | 
         | Unfortunately this is not possible (AFAIK) in TypeScript
         | currently, and will not be possible with TypeScript's current
         | philosophy.
         | 
         | (The above example is a hypothetical TypeScript compiler that
         | might support "templated" generic functions; with just RTTI
         | TypeScript could accomplish the same thing in a non-templated
         | function by passing in `T` as a function parameter at runtime
         | and doing runtime comparisons on `T`.)
        
           | TheRealPomax wrote:
           | Here's the thing though: if you wrote your TS properly, _you
           | don 't need this_ and asking for it just highlights that
           | you're not using TS the way it's meant to be used.
           | 
           | The only place you need runtime type enforcement (when you're
           | writing your own code in TS) is for validating third party
           | data at the point where you're ingesting it into your own
           | code. Once it's in there, _it is type safe_ if you used TS to
           | compile your code to JS, because your function calls and data
           | copies and everything else that moves data around was defined
           | in terms of compatible shapes.
           | 
           | And we already have runtime validation libraries to cover
           | that validation step. So many of them.
        
             | Nullabillity wrote:
             | That boundary validation is where that kind of reflection
             | would be useful! And it's a problem that (nearly) every
             | useful application has to face.
             | 
             | Well that, and getting rid of the silly "must be able to
             | strip types without processing them" design ideology would
             | also enable stuff like typeclasses.
        
             | theteapot wrote:
             | > And we already have runtime validation libraries to cover
             | that validation step. So many of them.
             | 
             | Did you read the article? I think that's the point. The
             | premise is _Typescript_ should be responsible for solution
             | to runtime validation against _Typescript_ not third party
             | hacks.
        
             | thanzex wrote:
             | Except for the fact that usually there is not just one
             | single typescript agent working within itself, for which
             | you don't need validation. There are many cases in which
             | you need to verify some object, anything that does not come
             | from your code i would argue is untrusted, I come across
             | this almost daily, it would be absolutely fantastic to just
             | have a way to check _does this object conform to this
             | type?_ Instead, i need to use some external dependency,
             | effectively duplicate my type definitions and add yet
             | another place to introduce bugs.
        
               | IggleSniggle wrote:
               | Just use a run-type transformer like typia, hooked right
               | into typescript-compile. Get your runtime type validators
               | generated from nothing but the typescript definitions.
               | 
               | Alternatively, use a runtime validator the provides good
               | type inference out of the box so you're still only
               | declaring your types once.
        
             | moduspol wrote:
             | And presumably you need to also trust your third party
             | libraries, unless you're also compiling them from
             | TypeScript. Right?
             | 
             | And even if you are--that might also involve ensuring their
             | tsconfig.json is compatibly similar to yours. Otherwise the
             | compiler might allow them to return null instead of the
             | object they say they return, among potentially many other
             | "gotchas" that are bound to appear.
             | 
             | EDIT: Though I think I do agree with you, ultimately.
             | Runtime type checking imposes non-negligible costs and
             | complexity that still theoretically should be able to be
             | guaranteed at compile time for cases where one isn't
             | validating untrusted user input.
        
               | andix wrote:
               | Yes, you need to trust your third party libraries, better
               | check them. Otherwise they may steal your data, inject
               | XSS, mine cryptos, have memory leaks or faulty logic.
        
           | RyanCavanaugh wrote:
           | Not to be flip, but if it were really all this easy, we would
           | have done it already.
           | 
           | There are dozens of questions you can throw at this code:
           | What if the input's a union? What if it's a nested union --
           | how do you avoid combinatorial explosion? What if the input
           | is a function -- how do you validate its parameter types
           | using runtime information? What if the input is a conditional
           | type? What if you're inside a generic function? The list is
           | enormous and it quickly gets into "you've dug too deep and
           | unleashed a Balrog" territory once you get beyond the
           | primitives.
        
             | twosdai wrote:
             | Can we just start with primitives and see what happens.
        
               | bastawhiz wrote:
               | JavaScript already has this. It's called typeof.
        
           | paxys wrote:
           | I still don't see the point. There is a long list of
           | libraries that do exactly this, and do it well enough. The
           | author has linked to them himself. The overall benefit of
           | this would be _maybe_ slightly better syntax for these
           | libraries (even that is doubtful, because plenty of them
           | already have a `reflect <T>()` interface), but still zero
           | runtime benefit. And getting it to be accurate for 100% of
           | cases would entail exactly what I mentioned - writing an
           | entire runtime to do this inference.
        
             | Nullabillity wrote:
             | So now you're stuck writing for an awkward inner platform
             | language rather than using the TS syntax itself.
        
               | paxys wrote:
               | What's this "awkward inner platform language"? They are
               | asking for a `typescript.generateRuntimeType<T>()`
               | function to be native to the language. Well plenty of
               | libraries provide _exactly_ this syntax today. Here 's
               | tst-reflect: `const type = getType<T>()`. Notice any
               | difference?
        
               | andix wrote:
               | Wow, i didn't know that this is already possible with
               | type script. seems like there are transformers you can
               | add to typescript that can implement it.
               | 
               | So type script more or less supports the feature they
               | asked for. Just not bundled with the main package, but
               | they provide the interface to get it done with 3rd party
               | transformers.
        
           | scotty79 wrote:
           | If that's so much needed I wonder why there isn't a thriving
           | ecosystem of pluggable typescript preprocessors that add
           | whatever values based on types direcly to typescript source
           | before compilation.
        
           | andix wrote:
           | It would be great if McDonalds would serve first class sushi,
           | and pizza, and seafood, and pasta, and steak, and if they
           | would deliver frozen meals, and iron your laundry, and ...
           | 
           | They just serve burgers and fries. And they are doing very
           | well with that strategy. Same goes for TypeScript.
        
         | cjdell wrote:
         | TypeScript does emit runtime type information through enums
         | though.
        
       | Tade0 wrote:
       | We have types at home.
       | 
       | My litmus test regarding whether I can even consider using a
       | library in production is: does it have more stars than
       | fartscroll.js?
       | 
       | https://github.com/theonion/fartscroll.js/tree/master
       | 
       | Most of the entries there are below that number, which begs the
       | question: is this really that much of a problem that it mandates
       | the drastic changes required?
       | 
       | I actually don't know if it's even doable, considering TS's
       | structural type system, where the answer to the question "what
       | type is X?" is not straightforward.
        
       | lucidone wrote:
       | OP and those who feel a similar sentiment should give C# a shot,
       | it's what they want regardless of whether they know it or not.
        
         | anonzzzies wrote:
         | Yep, but you want to write frontend with it then. Blazor (or
         | whatever liveview-like implementation) or wasm might fix that.
        
       | marcelr wrote:
       | Please no, typescript is already complex as all hell.
        
       | can16358p wrote:
       | This shouldn't happen... unless someone finds a way to do this
       | without a runtime overhead, which naturally seems impossible.
       | 
       | If this is done, TS will become something entirely different as
       | it currently doesn't have a runtime: it's a huge-and-smart
       | linter.
       | 
       | But that's all about it. No matter how huge, it's a linter.
       | 
       | Reflection would imply something beyond that. Even though I
       | absolutely love reflection, I believe it should not be integrated
       | into TS.
        
         | mrkeen wrote:
         | No runtime necessary, if I understand the problem.
         | 
         | Input is bytes. Bytes will never carry their type information
         | (and if they did, you couldn't trust them). So the type
         | information needs to be used in the Deserialiser, not its
         | input.
         | 
         | To get typed deserialisation of Foo, the compiler needs to
         | generate a FooDeserialiser automatically for you. The compiler
         | can't depend directly on Foo (Foo is written after the
         | compiler), but if Foo could somehow emit its type information
         | during compilation, then the compiler could depend on _that_.
        
       | orangepanda wrote:
       | I vaguely remember one of typescripts developers stating that if
       | they had to start it all over again, enums would not be added; as
       | enums are the only thing emitting runtime code.
       | 
       | Typescript does not change runtime behaviour, there is no special
       | typescript {#if}
        
         | ragnese wrote:
         | > I vaguely remember one of typescripts developers stating that
         | if they had to start it all over again, enums would not be
         | added; as enums are the only thing emitting runtime code.
         | 
         | It's true. I don't remember if that was the only/main reason
         | they said that, but that was definitely one of the things.
         | 
         | I find that a weird sentiment, though, because TypeScript has a
         | couple of other features that are not just "JavaScript + type
         | annotations".
         | 
         | Namespaces is one. It also has a syntactic sugar for defining
         | class properties in the constructor arguments. And it had that
         | experimental decorator feature for a long time, but I never
         | used it, so I don't know much about it. It also has the "`this`
         | parameter" syntax for methods/functions, which does disappear
         | at compile time, but still _looks_ and feels like more than
         | just a JavaScript function with type annotations.
        
           | WorldMaker wrote:
           | From my understanding, namespaces are on the same list with
           | enums of things that the developers regret ever adding to the
           | language but can't take out without breaking old code.
           | Namespaces also at least have the excuse of being a
           | "necessary" jQuery-era "Production pattern" in the land
           | before ESM was standardized and somewhat relating to the
           | similar syntax sugar of import/export when generating AMD,
           | UMD, and CommonJS modules.
           | 
           | So too are "experimental decorators" another thing that some
           | of the developers seem to list as massive regrets. That
           | example is even so much worse than namespaces because it
           | wasn't justified by existing patterns used in Production JS
           | at the time _and_ that they even believed that requiring a
           | compile-time flag with the word  "experimental" in it would
           | _stop_ developers from using that compile-time flag in
           | _anything_ destined for Production usage. (Seriously, we all
           | should shame the many projects /companies that did that.)
        
           | wvenable wrote:
           | Although namespaces are not deprecated they are not
           | recommended anymore -- much for the same reasons.
        
       | evmar wrote:
       | If anything, the wide range of different approaches to run-time
       | type information listed here is pretty good evidence that there
       | are many different use cases and any approach built in to TS
       | would not cover them all.
        
       | drtz wrote:
       | The problem with TypeScript isn't the lack of reflection. The
       | problem is that at runtime its type system is still Javascript in
       | all its glory. Workarounds are just band-aids on the underlying
       | mess at best.
        
       | kazinator wrote:
       | Anything run-time has to come from JavaScript.
       | 
       | TypeScript is syntactic expansion layer over JavaScript.
       | 
       | "C preprocessor, please give me arrays that know how big they are
       | at run-time. Oh, and access to the calling function's local
       | variables!"
       | 
       | "Common Lisp defmacro, give me continuations usable anywhere!"
       | 
       | A macro layer can bring you the pie in the sky, but only (1) at
       | some nonozero cost and (2) with additional representations that
       | are not understood by regular code that is not in that framework.
       | 
       | Suppose a TypeScript type object is attached to every run-time
       | object created in TypeScript. Firstly, those objects become more
       | bloated and expensive to construct. [Edit: not really: all
       | objects of the same type can have a pointer to the same meta-data
       | which is created once]. They will need run-time support in their
       | execution environment in order to have those type
       | representations. Objects not created by TypeScript-generated code
       | will not have anything like that attached to them, so places in
       | the system where the two domains interoperate won't be able to
       | rely on reflection; it will need a fallback for objects that
       | don't do reflection.
       | 
       | I think serialization can be done without reflection. Because at
       | the time when the static language is being processed, you could
       | annotate certain types as requiring serialization. Then for those
       | types, TypeScript would generate the marshaling routines.
       | 
       | Generating marshaling stubs from an interface language has been
       | done in the C world for decades. It doesn't require any run-time
       | types. The generated marshaling stubs just know how to walk
       | objects of the type that they handle, and that's it.
       | 
       | It looks like in TypeScript, serialization is just punted to
       | JSON, which is inadequate because when we are deserializing, we
       | want to check that the JSON blob has a type and shape compatible
       | with the TypeScript-level type. (Or even for that that type to be
       | inferred in some way.) There are some libraries that try to do
       | something in this direction like ts-serializer.
       | 
       | I think it's something you really want in TypeScript itself.
       | 
       | Basically round up the half-dozen or so use cases for reflection,
       | and provide for all of them somehow without actual reflection.
       | It's an X/Y problem. People really want to do Y (e.g.
       | serialization), and believe that they must first solve X (have
       | metadata in objects for reflection).
        
       | medler wrote:
       | I'm mainly a backend programmer, so I know just a little
       | typescript, but I don't really understand what they're asking for
       | here. It sounds like they want to serialize and deserialize
       | typescript types automatically? If so, that sounds like a good
       | fit for a library, not something most languages offer. (Please
       | educate me if I'm missing the point)
        
         | jfengel wrote:
         | Javascript already has standard serialization and
         | deserialization tools.
         | 
         | The problem is that the deserialization is untyped. It's just a
         | Javascript object. Usually, the first thing you'll do is to
         | cast it to a typed Typescript definition, but there's no way to
         | guarantee that it actually fits that definition. No type
         | information exists at runtime. It's used solely to check the
         | code itself during compilation.
         | 
         | So any time you get data from outside of your program (over the
         | network, from a database, out of a config file, etc.) you just
         | have to hope that it actually fits the type. You can write code
         | to check it... but you have to write that code yourself.
         | 
         | There are libraries you can use. For example, you can use a
         | Data Description Language with a simpler type system, which
         | usually suffices for the kind of data you want to serialize.
         | Then you can use that to generate both a Typescript definition
         | _and_ a runtime type checker. But that 's inelegant, and not
         | standard, so every project is different despite it being
         | something everybody needs.
        
           | andix wrote:
           | It's basically the same for typed languages. If you want to
           | map JSON to a typed data structure, you need a serialization
           | library to do that.
           | 
           | JavaScript has a very simple built-in json reader/writer that
           | does no type checking and returns some nested dictionaries or
           | arrays. If you want more, you need a library (for example
           | zod). Or you just trust the data to have the right shape.
        
             | jfengel wrote:
             | Java has runtime type definitions, which you can use to
             | automatically type check deserialized objects. With
             | reflection you can write that in ordinary Java.
             | 
             | I imagine C# and other Java-esque languages have something
             | similar. But Typescript deliberately avoids that, and it
             | would be difficult to get it to do so.
        
         | shortrounddev2 wrote:
         | There is currently no good way to automatically validate the
         | object input to a typescript API. In the way that you can
         | simply specify the class of a POST body to a spring boot
         | controller or Asp.Net controller, every class in typescript
         | requires you write the type information twice: once for the
         | typescript compiler, and once again for the runtime
         | deserializer, in the form of decorators. Having to write extra
         | code to make up for a deficiency in your framework, runtime, or
         | language is the definition of code smell. As such: all
         | typescript backends which attempt to properly validate user
         | input are guilty of egregious codesmell.
         | 
         | Additionally, because it is impossible to emit these types to
         | the runtime, even in the form of a string naming the type, you
         | cannot validate input to an API using generics. If you have,
         | say, a class which will run a query for a record based on the
         | keys in that record:                   class Query<T>{
         | filter: Array<keyof T>         };
         | 
         | The typescript compiler can validate if a string in "filter"
         | is, in fact, a key of the class T. However, you cannot write
         | any decorator on this class to make the information of what
         | keys exist on T available at runtime. Therefore, to write such
         | a query interface and validate it at runtime, every record
         | which can be queried with this object must have its own
         | specific "[Record]Query" class written with its own decorators
         | to validate every possible input, usually with the valid keys
         | hardcoded into the decorator.
         | 
         | Because there is no real relationship between the class and the
         | runtime validators, this makes validation more error prone,
         | because it relies on either human beings to consistently
         | remember to add new fields to the relevant decorators, or a
         | custom linting tool to be written which basically pre-processes
         | the typescript source using the typescript API. This is, again,
         | writing code to account for the lack of features in the
         | framework.
         | 
         | WHY do we put up with this crap? We have had Java and C# for a
         | long time, and they solved these problems a long time ago. Why
         | are we writing Typescript on the backend?
         | 
         | The simple answer; it is easier to find javascript developers
         | than it is to find java developers. Bootcamps don't teach Java
         | or C# because there's more react jobs out there. Bootcamp grads
         | are not generally coding enthusiasts and so re-tooling is
         | difficult for them and they don't generally do it for fun. So,
         | we decided to write our backend in Javascript (or Typescript)
         | as a _labor_ decision, not a _technology_ decision.
        
       | moritzwarhier wrote:
       | Very interesting post to me. There always was the argument that
       | TS should be erasable and carry no runtime information, enums and
       | decorators are a mistake, etc.
       | 
       | For what it's worth, I want my production bundle to be as small
       | as possible. Most people use bundlers among TypeScript, which
       | further complicates things.
       | 
       | I would love a _standard_ library to e.g. generate type predicate
       | functions from TS types.
       | 
       | Taming the beast from this end might even end up useful for
       | things like compilers, no?
       | 
       | TS provides a nice mechanism to be strict in the frontend and
       | validate types using runtime code, that is, type predicates.
       | 
       | But from a higher level, this has never been enough, and you want
       | to map JSON to TS types using some kind of schema definition.
       | 
       | I would love to just be able to pass in a TypeScript type and
       | generate its type predicate at compile time.
       | 
       | Unfortunately, this is impossible due to the halting problem.
       | 
       | So, a _standard_ solution for the case of a plain serializable JS
       | /JSON object and the according TS interface would be very much
       | welcome.
       | 
       | Edit: and all of this is of course about data from external
       | sources (requests, DOM, compiler...). For all of the code and
       | data known at compile time, there is rarely a need to validate
       | types, as they are statically analyzable.
       | 
       | So there is a huge scope that TypeScript has to cater to. It
       | includes JS projects where external type definitions often become
       | obsolete and people who code in TS but do not enable strict mode
       | for their own code (that is, disallow "any"). And all of these
       | type defintions have to be interoperable.
       | 
       | For API requests, this is basically calling for a default generic
       | REST client, and I agree that would be useful. The REST client
       | could use type predicate functions extensively.
        
       | anonzzzies wrote:
       | Thanks for this. I thought I was screaming into a void. I started
       | with typescript in 2018 and after two years started to believe
       | this is just not really a solution because what it is. I came
       | from Haskell/c#/f#, and TS, while it has a very powerful type
       | system, just doesn't give you much of the advantages the above
       | languages give you outside (some of) dev. Even when it does, it
       | is really limited in practice (aka when dealing with the real
       | world) vs, let's say, c#. You can defend from that somewhat,
       | however, you have to always keep the extremely (and that's
       | intended, but not what it could be) leaky abstractions in mind to
       | not run into bugs that shouldn't be possible in something that
       | claims static typing (if you don't know that it drops to js
       | without checks after compile).
        
         | seanmcdirmid wrote:
         | The point of TypeScript was always to run on web, it wasn't
         | meant to give you a language advantage over say, C#. You use C#
         | if you have to develop for .NET, you use TypeScript if you have
         | to develop for Web, you wouldn't use one or the other in
         | another context.
         | 
         | That being said, TypeScript is differently capable from C#. C#
         | is nominally typed, with decent reified generics. I like
         | programming in C#, and miss its features when programming in
         | TypeScript. TypeScript, on the other hand, is structurally
         | typed, forgoes soundness, and comes with a powerful type system
         | to express complex type relationships (again, possible because
         | it forgoes soundness). I like programming in TypeScript also,
         | and also miss its features when programming in C#. I doubt a
         | language that combined C# and TypeScript would be very nice,
         | these languages go in different directions to good effect, but
         | they are not compatible directions.
        
           | anonzzzies wrote:
           | But that's when you focus on the type system theory and I
           | agree with that. I don't see how enforcing types after
           | compilation has anything to do with a programming language
           | for the web vs 'not for the web' though, but I understand it
           | is the philosophy to check the types and then drop to plain
           | JS. Best tool for the job, sure, but literally losing
           | everything typed you wrote and designed after compilation is
           | just not as useful as it could (should imho) be. You express
           | whatever, it compiles, and then everything can break all that
           | you carefully set up, without any errors.
           | 
           | That's not the language or type system per se, it's the
           | compiler implementation or rather the philosophy to stay
           | close to js and have a very thin (powerful) layer at compile
           | time only. Typescript in wasm, Deno maybe (didn't try yet)
           | etc could change this. Then we can have types as in other
           | languages. Not sure why this would not be a win for everyone
           | vs the current status.
           | 
           | Also, as you know, the run the web went out the door with
           | nodejs; many backends are now typescript and indeed competing
           | (... for at least writing web apps) with c#.
        
       | jmull wrote:
       | I'm kind of doubting the real value of this.
       | 
       | For validation, ORMs, APIs, etc., once you move beyond the
       | simplest cases, you need more information than is present in
       | Typescript type information. And that's before we get to the app-
       | specific concerns.
       | 
       | So you're going to end up with schemas, boiler-plate, code-
       | generators, and/or glue code anyway. Not to mention the libraries
       | to help manage this stuff.
       | 
       | It's not that it's completely unhelpful, but I think it's a
       | partial solution, and not the tricky part either.
        
       | nycdotnet wrote:
       | The TypeScript language service which powers IDEs and which is
       | maintained/shipped with TypeScript already has the knowledge of
       | all the types in your application. It seems like it would be
       | relatively straight-forward (though a lot of work!) to develop
       | some sort of code gen library that uses the TypeScript types
       | known to the language service to reflect on those types and emit
       | some sort of runtime type validation functions as part of a build
       | step. This could be done in an npm module similar to webpack or
       | in an IDE plugin. If that functionality doesn't exist today given
       | all the listed open source projects, I'm kind of surprised. I
       | don't think the TypeScript team would need to do anything to
       | allow such a project to be developed.
        
         | benatkin wrote:
         | It has quite an awkward way of being distributed. If you google
         | for standalone TypeScript language services you'll find
         | unofficial ones. Deno's is much simpler. Maybe they should
         | pioneer this.
         | 
         | Edit: Actually this is the first result, I hadn't tried those
         | exact terms. It's a bit hairy though, which is why the
         | unofficial typescript-language-server npm package wraps it.
         | https://github.com/microsoft/TypeScript/wiki/Standalone-Serv...
        
       | brundolf wrote:
       | 2C/, having non-runtime types makes code behavior easier to
       | reason about, especially when aggressive type inference is
       | present, and I'd hate to lose it
       | 
       | One of the things I don't love about Rust is how type inference
       | affects program behavior in ways that can be really subtle. In
       | Rust's case this is a necessary evil because it doesn't have a
       | dynamic foundation, but TypeScript has done great without that
       | compromise
        
       | LispSporks22 wrote:
       | I've heard the same complaint about Elm
        
       | lolinder wrote:
       | > Type erasure is good! It means JavaScript project can consume
       | TypeScript projects without any knowledge of TypeScript. It's
       | just emitted JavaScript. This does not mean you can't emit the
       | type information separately in a consumable lookup table that's
       | separate from the code. The lack of this type information means
       | we use esoteric libraries which ultimately pollute the JavaScript
       | with all the convoluted typing working arounds... soo... type
       | erasure has defeated the purpose of type erasure. It's a second
       | order effect, where the design goal defeats the design goal. :(
       | 
       | I think the author misunderstands the design goals of TypeScript.
       | The goal isn't to have pristine, beautiful JS code with no
       | complexity, the goal is to have the runtime semantics of
       | TypeScript be identical to the runtime semantics of JavaScript.
       | Barring the regretted exception of enums, TypeScript can be
       | converted to JavaScript by simply stripping type annotations.
       | _That 's_ the design goal, and that goal is not in conflict with
       | itself.
       | 
       | The ecosystem that has built up around TypeScript depends on
       | complete type erasure, and much of it would evaporate if this
       | wish were granted. TS support in ESBuild, Deno, and Bun would
       | become nearly impossible, because each would need to re-implement
       | all of tsc in their respective languages, while right now they
       | just need to maintain their own simplified parsers.
       | 
       | On the other hand, the convoluted libraries that OP bemoans are
       | perfectly compatible with all of these tools, because they're
       | implemented in user space.
        
         | shortrounddev2 wrote:
         | There are plenty of ways to emit runtime type information
         | statically without a runtime. `keyof` could statically convert
         | to the list of keys in a class. Hell, even typescript classes
         | could do what ES6 classes do and initialize class keys to
         | undefined. Currently, typescript classes erase all keys unless
         | explicitly defined. Object.keys() on a newly instantiated
         | typescript class without set members will return nothing, while
         | an ES6 class will return all of the keys. Just an option in
         | tsconfig.json to convert TS classes to ES6 classes (or just
         | allow ES6 classes to exist at the same time in Typescript)
         | would make some code generation WAY easier.
         | 
         | Or some kind of syntax to emit the name of a type as a string.
         | Currently, if you run `typeof foo` it will return the type as a
         | string. If you could have a static version of that, like
         | `tstypeof foo`, or with a class:                   class Foo {
         | bar: string;         };              console.log(tstypeof
         | Foo::bar) // or something less C++-looking; compiles to
         | "string".
         | 
         | then it would be killer. This could break compatibility if you
         | change the type of a field and a compiled client using the code
         | were to expect "string" when it's been changed to "number". But
         | I'd rather live in that world than the one we live in now
         | 
         | Just some _basic_ RTTI would make a lot of ugly codesmelly
         | boilerplate Typescript code evaporate instantly.
        
       ___________________________________________________________________
       (page generated 2023-07-07 23:00 UTC)