[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)