Remix.run Logo
tempodox 5 days ago

It looks like this works by stripping away the type information, so at best it saves you a transpilation pass and doesn't improve safety.

nine_k 5 days ago | parent | next [-]

One of Typescript's design goals is that removing all type-related parts of the source text should yield a valid JavaScript file. A typescript compiler does not generate code (unlike, say, PureScript).

You can run a typechecker (such as tsc) that check various properties of your code statically, relying on the type information. It is then erased.

The same applies, say, to Python: type annotations are ignored at runtime. Somehow similarly, Java's type information is also partly erased in the bytecode; in particular, all the information about parametrized types. (This is to say nothing about actual machine code.)

9rx 5 days ago | parent | next [-]

> A typescript compiler does not generate code

Except for where it does: Enums, namespaces, parameter properties, etc.

MrJohz 5 days ago | parent | next [-]

This is true but these are also old features, and the TS team have stated that they will not add any more features like those, and to a certain extent regret adding them initially (particularly decorators, which iirc were added because the Angular framework wanted to use them). You can also see that these features aren't really being updated to match recent Typescript developments (parameter properties can't do true private properties, const enums don't work with isolated modules, etc).

I don't think those features are ever going to go away, because they've been around for so long and are so widely used. But I generally use erasableSyntaxOnly in new projects, because I find it's useful when my typescript source matches the generated Javascript code as much as possible.

lifthrasiir 5 days ago | parent | next [-]

> I find it's useful when my typescript source matches the generated Javascript code as much as possible.

Is this that worth? In the past I was able to read past async/await desugaring generated by TypeScript, and there are several useful non-JS syntaxes that are much easier to read than that (e.g. enums). Of course it would be great if ECMAScript eventually adopts them, but that doesn't seem like a strict requirement for me.

MrJohz 4 days ago | parent [-]

Async/await was implemented according to the ECMAScript spec - for a while it existed in typescript but not yet in browser runtimes, but it was fully specced out as a javascript feature rather than a typescript one. And when configured to emit ESNext JS files, typescript would emit the async/await calls verbatim, rather than transpiling them. After all, at that time async/await was valid, spec-compliant javascript (that hadn't yet been implemented in all browsers).

This is true for a number of features, most recently explicit resource management, that required syntax that was part of ECMAScript, supported in typescript, but not yet implemented in mainstream runtimes. In these cases, typescript is doing the same thing as Babel and converting new JS to old.

The syntaxes under discussion are ones that were implemented in typescript without an ECMAScript proposal, and typically are completely non-standard, and may well never be implemented in browsers, or if they are, be implemented completely differently. For example, the original decorators implementation was completely different to the one now being proposed, and enums could well end up going in the same direction.

This confusion about what is typescript, and what is javascript is exactly why I think avoiding the typescript-only features is a good idea. I've seen a lot of people make this mistake (and it's a very reasonable mistake to make!) and I think it's easier to explain what typescript is actually doing if you make it clear that it's only doing type-level stuff.

marcjschmidt 4 days ago | parent | prev [-]

> because I find it's useful

How useful is it exactly that you accept to not use DX improving syntax like constructor properties, enums, etc? To me, someone who uses these features _a lot_, this would be a terrible trade. Seems more like people push this out of ideology and because TS is never going to be part of node itself (since its implementation is just way too slow)

MrJohz 4 days ago | parent | next [-]

It's useful as a tool for communication and simplification. Ignoring those features, typescript is just javascript with type annotations, which makes understanding how something works easier. I've worked with a number of developers who have been confused about what's a typescript feature, and what's a javascript feature, because this boundary feels blurred (particularly when working with modern ESNext syntax that might not be implemented in browsers but is understood by typescript). Being able to say clearly: everything after a colon (and some other places) is typescript, everything else is javascript is a great way of helping people understand what's going on.

In terms of the tradeoff, I rarely, if ever, use those features in the first place. For enums, I'd rather just use string literal types, which are slightly easier to write and compose, potentially assigning them to variable names if I think that's clearer for a given use-case. For constructor properties, they don't work for private properties which is the most common property I'm writing, so I don't really see the value.

The two other features that are disabled are namespaces and modules that contain code, and `import ... =`, both of which have clearer JS alternatives.

FWIW, you can enable those features in node, although that's still behind an experimental flag. It's marginally slower (because the translation is slightly more complex than replacing types with whitespace) and it requires sourcemaps under the hood, but if you really want those features you can have them. Or you can use an alternate runtime that supports them out of the box (Deno, Bun). But even then, I think they make teaching and understanding the language more complicated and I wouldn't personally recommend them. (Assuming someone might listen to the personal recommendation of a random HN comment!)

epolanski 4 days ago | parent | prev [-]

Enums are crap and unsafe use union types.

mirekrusin 5 days ago | parent | prev [-]

Just use erasableSyntaxOnly = true and you're fine.

mmmmbbbhb 5 days ago | parent [-]

Hadn't heard of that. Thanks

hmry 5 days ago | parent | prev | next [-]

Agreed about TS, but Python type annotations are not ignored. They are executed as code (all type annotations are valid expressions) and the results are stored on the module/class/function object that contains the annotated variable

nine_k 4 days ago | parent [-]

Python type annotations get turned into metadata which other tools may inspect at runtime, but the Python runtime itself does nothing with it. It's just well-structured comments.

In Python basically everything is executable, and so are type annotations.

throwitaway1123 4 days ago | parent [-]

Somewhat related: you technically can access some type metadata in TypeScript at runtime using the `emitDecoratorMetadata` and `experimentalDecorators` tsconfig options, along with Microsoft's `reflect-metadata` polyfill package. There was a trend at one point where people were writing database ORMs using decorator metadata (e.g. Typegoose, TypeORM, and MikroORM).

This of course requires your build tool to actually understand the TS type system, which is why it's not supported in tools like esbuild and tsx (which uses esbuild under the hood).

kamov 5 days ago | parent | prev | next [-]

Except in Python you can easily access the type hints at runtime, which allowed people to build ergonomic libraries such as Pydantic

fnord77 5 days ago | parent | prev [-]

> Somehow similarly, Java's type information is also partly erased in the bytecode;

Just for generics, I believe

stevage 5 days ago | parent | prev | next [-]

>so at best it saves you a transpilation pass and doesn't improve safety.

That's a bit misleading. Node being able to run TS code does not "improve safety", because that's not where the type checking happens. You can do type checking in your editor, or various other points in your toolchain.

Node being able to run TS code reduces the friction in writing TS code, which indirectly helps with type safety.

marcjschmidt 4 days ago | parent [-]

Except it doesn't. In anything serious, you have to wait for a full type check to happen before you run your TS code. Why would you run code that has not been checked yet and could throw very weird errors like undefined property access?

That just doesn't make sense. Yes, you can wait for your editor in your current open file, if you are lucky and the change in the open file doesn't break anything downstream in another file that is not yet open. In best case you have such simple code that nothing breaks, and in worst case, you have to still run it with type-checking - on top of running it in type-stripping-mode, because you got weird errors in runtime. This is a net negative.

This whole situation is there because we are trying to workaround the slow TSC. It's not a feature, it's something we actively work around. We try to whitewash now the obviously less useful "solution" of running code without its core features enabled: type checking. To me this is insane.

SCdF 4 days ago | parent | next [-]

You can tsc on the code and then ship that git hash if it passes. You don't need to run it every single time the code executes, nothing of value is gained, because nothing has changed.

MrJohz 4 days ago | parent | prev [-]

This is not my experience at all. In my experience, it's often quite useful being able to run code that doesn't fully type-check, as long as I do make sure everything's correct by the time I commit it. For example, I might be refactoring a module with some tests, and make a change that breaks the tests and some code in some other module. At this point, I often go in this order:

1. See that the tests aren't type checking correctly (usually for an obvious reason like adding an extra parameter or something). 2. Fix the tests using the type hints. 3. Run the tests to make sure my refactoring made sense and didn't break anything unexpected at runtime. 4. Fix all the uses in other modules.

Step 3 requires me to be able to run code that doesn't type-check correctly, and that's a useful feature.

There's also similar cases where I want to see how something looks in the UI even if it's not properly hooked up yet and causing type errors - I can check that part of the UI works and that it throws the correct runtime error (equivalent to whatever error typescript has). I've also had cases where I've cast things to `unknown` because I don't want to figure out the type just yet, and then written an implementation that is filled with typescript errors but will work at runtime as a mini proof of concept, only to later go back and get the types right.

I shall think you're underestimating how important fast cycle times are. When I'm developing, I normally have my linter, tsc, the dev server (tsx or vite), and the test runner all running simultaneously in watch mode. At any one point, I'm probably only interested in the output from one of these tools (the type checker until the types are all correct, then maybe the test runner until everything's green there). But if I run all of them at once, then they all run optimistically, and the tool I'm interested in is more likely to give me immediate feedback. That's really useful! Even with the new 10x typescript compiler, I'd still rather my tests start running immediately rather than waiting for another process to start and finish before they get going.

sdfhbdf 5 days ago | parent | prev | next [-]

TypeScript never promised improving safety, maybe it’s a common misconception. But TypeScript has no runtime mode or information. You were always at the mercy of running and not ignoring the typechecker. Nothing stopped you from running ts-node or tsx on code with egregious type errors. TypeScript is more like a linter in that regard.

MrJohz 5 days ago | parent | next [-]

I think it's not fair to say that Typescript isn't about improving safety, just that the mechanism isn't the same as with other languages. Typescript had always allowed you to ignore the type checker (in fact, the default configuration will always attempt to emit compiled Javascript, even if the source Typescript has type errors). But if you run the type checker on every commit (via e.g. CI or a precommit hook), then you can be sure that the code you release is correctly typed, which will not guarantee it is safe, but makes it more likely.

I agree that it's better to think of Typescript as a linter that needs specialised annotations to work, rather than a type system like you might find in Java or Rust.

tempodox 4 days ago | parent | prev | next [-]

> TypeScript never promised improving safety

What, pray tell, would be the point of putting all that type information in there, and then have it checked (via tsc), if not for the sake of safety? What other use would this have in your opinion?

tkzed49 5 days ago | parent | prev [-]

> TypeScript is more like a linter

that's exactly the point--GP is pointing out that node can't do that part

madeofpalk 5 days ago | parent [-]

Which is why the title is "Node can now execute Typescript files" and not lint, check, or even run TypeScript files.

ohmahjong 5 days ago | parent [-]

I'm not sure what the distinction between "execute" and "run" is; is there a difference?

madeofpalk 4 days ago | parent | next [-]

No, I don't think there really is. But to be execute is even more clear that it's just... executing the code, whereas I could maybe understand someone being confused that run implied some level of type checking.

JoBrad 4 days ago | parent | prev [-]

Node is just inline transpiling the TS into JS, then running the JS.

marcjschmidt 4 days ago | parent [-]

This is misleading. It is not transpiling TS in JS, it is transpiling a subset of TS into JS. If my normal TS code can not be "executed" by Node, then it is not executing TS per definition but something else. If you are good with Node supporting and "executing" only a subset of TS and lacking useful features, that's fine. But don't tell people it is executing TypeScript. That's like me saying my rudimentary C++ compiler supports C++ while in reality only supporting 50%. People would be pissed if they figure it out once they try to run it on their codebase.

Dylan16807 4 days ago | parent [-]

It's quite close to 100%. It makes one extra-strictness toggle mandatory. Saying it's not TS is much more misleading.

resonious 5 days ago | parent | prev | next [-]

Not needing a separate compile step just to run some script sounds great to me. I will run tsc if I want a type check.

marcjschmidt 4 days ago | parent [-]

There is always a compile step (JS -> Bytecode -> Machine code). The question is only if it is visible to you or not. They could have made it totally transparent to you by fully support TS including type checking under the hood including support full TS and not this subset of it, but decided not to do so. There is nothing inherently great to have less compile steps if you are not even aware of it. See v8 how many compile and optimizations steps they have - You don't care, because you don't see it. The only problem of TS is, you will always be able to see it because of it being slow.

I think running TS without type checks is almost entirely pointless.

resonious 4 days ago | parent [-]

The point is I don't have to deal with the index.js blob that gets produced by running the compile step myself. Worse yet the source maps. It's significantly less steps so pretty helpful I'd say.

ryuuseijin 5 days ago | parent | prev | next [-]

I'm using tsx for a project to achieve the same effect. As you said, it saves you from having to set up a build/transpilation step, which is very useful for development. Tsx has a --watch feature built in as well, which allows me to run a server from the typescript source files and automatically restart on changes. Maybe with nodemon and this new node improvement this can now done without tsx.

To check types at runtime (if that can even be done in a useful way?) it would have to be built into v8, and I suppose that would be a whole rewrite.

insin 5 days ago | parent [-]

Node has had a built-in --watch flag for a while too:

https://nodejs.org/docs/latest/api/cli.html#--watch

linhns 5 days ago | parent [-]

Surprisingly this gone unheralded.

rand0m4r 4 days ago | parent | prev | next [-]

I agree, that said if the main reason people use TypeScript is security they should use a decent programming language instead.

medv 5 days ago | parent | prev | next [-]

Exactly! Type checking can be long and costly.

5 days ago | parent | next [-]
[deleted]
akkad33 5 days ago | parent | prev [-]

Ocaml does it fast

lbltavares 5 days ago | parent | prev | next [-]

Yes, it can't parse enums, for example.

beart 2 days ago | parent | next [-]

I think enums are on their way out, and erasable syntax is one of the main reasons.

https://dev.to/ivanzm123/dont-use-enums-in-typescript-they-a...

They added an optional flag to disable enums (and a few other features) in 5.8

https://www.totaltypescript.com/erasable-syntax-only

dust42 5 days ago | parent | prev [-]

It is available behind an experimental flag: --experimental-transform-types

reactordev 5 days ago | parent | prev | next [-]

Typescript mission is to strip itself of typing, what’s your point? Typescript will never be Java, you’ll never have runtime reflection.

mattlondon 5 days ago | parent | prev [-]

Yeah agreed - saying "node can execute typescript files" is a bit misleading. More accurate would be "node can find-and-replace type information with spaces from .ts files and try and executing them as if they were plain JavaScript"

I suspect this would only handle the most rudimentary and basic typescript files. Once you start using the type system more extensively I suspect this will blow-up in your face.

It's kinda a shame. What a missed opportunity to do it properly (rather than relying on third party plugins etc)

Edit: if you are just using typescript for type checking at build time (i.e. the most basic rudimentary typescript files) the sure fine this may help you. But typescript also generates JavaScript code for missing features e.g. proper class access modifiers, generics, interfaces, type aliases, enums, decorators etc etc. typescript generates JS code to handle all that, so you're going to have a bad time if node just replaces it with the space character at run time.

bitterblotter 5 days ago | parent | next [-]

Just to give context here, NodeJS doesnt support enums, namespaces and class parameter properties. All of these have been described as regrets by Anders Hejsberg, and none of them prevent advanced use of the type system at all.

Source: 49m 43s https://m.youtube.com/watch?v=NrEW7F2WCNA

marcjschmidt 4 days ago | parent [-]

Yes, they regret them because they hinder adoptions. Why? Because nobody chose to add TSC with all features in their runtime because TSC is extremely slow.

They know they can skyrocket adoption by limiting the language. That's the reason they regret it. This is just a strategy to increase adoption. Not because they are bad features. They are in fact very useful, and you should not stop using them just because your favorite runtime decided to go the easy way and only supporting a subset of TS by stripping types. You should rather switch the runtime instead of compromising your codebase.

koito17 5 days ago | parent | prev | next [-]

TypeScript 5.8 added a configuration option[1], where the compiler emits errors when using language features that result in code generation (e.g. enums). It's expected that people wanting to run TypeScript through "erase-only" transpilers will use this option.

Of course, everyone else is free to use enums, decorators, class parameters, etc., but for quick prototyping or writing simple scripts in TypeScript, Bun has been good enough for me, and I assume Node will be "good enough" as well.

[1] https://www.typescriptlang.org/docs/handbook/release-notes/t...

sdfhbdf 5 days ago | parent | prev | next [-]

> node can find-and-replace type information with spaces from .ts files and try and executing them as if they were plain JavaScript

That’s what all the other tools like ts-node and tsx do already.

I’m not sure what more are you expecting to do?

Typescript is build time type checked, there is no runtime component to TypeScript. If you want type checking you run tsc.

I think this is a great step in the right direction by node. It will save transpiration on the server and improve stack traves and whatnot.

> Once you start using the type system more extensively I suspect this will blow-up in your face.

I don’t see why. There isn’t any more runtime information in “complex” TypeSceipt types than in simple ones. It’s all build time - see above.

> What a missed opportunity to do it properly

Please explain in more detail what “doing it properly” means to you. Including the typechecker? If so that wouldn’t make sense - they would be competing with TypeScript itself, they shouldn’t, all the “third party plugins” rely on tsc for type checking.

dingi 5 days ago | parent | next [-]

> Typescript is build time type checked, there is no runtime component to TypeScript.

Not exactly. Typescript enums and decorators are examples.

marcjschmidt 4 days ago | parent | prev [-]

> I think this is a great step in the right direction by node

I think it's the opposite. It will be a net negative, since people will now run TS by default without type checking. Wasting so much time chasing weird runtime errors - just to end up running the full blown TSC type checking again. They will also write very different TS now, trying to workaround the limitation and arguably very useful features like Enums, constructor properties, etc. This has real negative effects on your codebase if you rely on these, just because Node chose to support only a subset.

It's interesting to see the strategy now and to see people even gaslighting people into believing no type checks and less features is a good thing. All just because of one root cause - TSC being extremely slow.

9dev 5 days ago | parent | prev [-]

I have been using this feature to remove the transpilation step during development for a rather large monorepo that makes extensive use of very complex types and haven’t noticed any errors whatsoever at runtime.

You’re downplaying this quite a bit. Node being able to execute TS files slashes a lot of tooling in half, especially during development when you want to modify files and retry in quick succession.

Besides, I’m not so sure this cannot be expanded in the future to adopt validation features before stripping the type information. For now it solves some major pain points for a lot of people.