Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why can't this feature do type checking at runtime? #205

Open
DonaldDuck313 opened this issue Dec 16, 2023 · 30 comments
Open

Why can't this feature do type checking at runtime? #205

DonaldDuck313 opened this issue Dec 16, 2023 · 30 comments

Comments

@DonaldDuck313
Copy link

According to the description of this proposal,

At runtime, a JavaScript engine ignores [type annotations], treating the types as comments.

I think it would be better if type checking were done at runtime. To be clear, I suggest that the type checking should be done when running the actual code similar to what is done in PHP, not when parsing it similar to what is done in TypeScript. So for example I suggest that this:

function f(x: string){
    //Function body
}

should do roughly the same thing as this:

function f(x){
    if(typeof x != "string"){
        throw TypeError("Expected parameter x to be a string, got " + x?.constructor?.name);
    }
    //Function body
}

You mention in your FAQ that

Implementing this proposal means that we can add type systems to this list of "things that don't need transpilation anymore" and bring us closer to a world where transpilation is optional and not a necessity.

While this is true for transpilation, if type checking isn't done at runtime as you suggest, the code will still have to be run through static type checkers. If type checking were done at runtime, however, it would allow to run the code directly in the browser in a type safe way without using any third-party tools to analyze it at all.

Is there any reason why you decided not to do this?

@tabatkins
Copy link

In short, it was decided against because it would mean that JS is responsible for defining and maintaining a type system. It looks simple enough with simple cases, like "x: string", but then you get into subclasses, generics, covariant and contravariant type variables, type functions, etc. It's a huge undertaking, much larger than just allowing the annotations themselves, and it would mean that the type system also ends up with the same back-compat constraints as the rest of JS, rather than allowing tools to experiment and upgrade over time.

@conartist6

This comment was marked as spam.

@tabatkins
Copy link

Tell that to Python, which has a thriving ecosystem of type annotation tooling that is also completely meaningless at runtime.

@shaedrich
Copy link

@DonaldDuck313 I thought, there could be a switch similar to use strict 🤔

but then you get into subclasses, generics, covariant and contravariant type variables, type functions, etc. It's a huge undertaking

@tabatkins But these wouldn't have to be added right away, would they?

@conartist6
Copy link

conartist6 commented Aug 15, 2024

It looks simple enough with simple cases, like "x: string", but then you get into subclasses, generics, covariant and contravariant type variables, type functions, etc.

I agree, but you could just as easily be listing all the additional information you need to know before the basic syntax has the same meaning to the person who wrote it as it does to the person reading it.

This is not possible, because the person reading does not have any information about what type system the types were written for. If I say "I am going to eat this sandwich," but to me the word "sandwich" means "baseball", you might think upon hearing this that we have communicated successfully. You'd think that right up until you saw me eat the baseball (after all we are using the same syntax).

@conartist6
Copy link

Of course this is all premised on the idea that the goal of language is to create communication.

Without creating communication the creation of language is impossible, and with the establishment of communication the creation of language is inevitable. Darmok and Jalad at Tanagra.

@tabatkins
Copy link

Yes, just like in Python (which has multiple typing tools that use similar, but not identical, syntax and behaviors, particularly for the complex stuff like what I listed), you have to know what typing tool the code is using in order to understand that code. This is not, in practice, an issue. (It's usually MyPy, occasionally PyType, checking the CI or the project metadata generally makes it obvious.)

@conartist6
Copy link

conartist6 commented Aug 15, 2024

@tabatkins Can you explain to me how you see this as bringing value to JS given that we already have type systems?

@conartist6
Copy link

conartist6 commented Aug 15, 2024

Another part of the reason I stand in opposition to this is that I sense that it is on some level a reaction to poor tooling design, which I do not feel is grounds for changing the language (but rather for changing the tools).

@tabatkins
Copy link

We do not have type systems in JS. We have separate languages that compile to JS which have type systems, but cannot be executed as JS. The parallel for Python would be if we had TypeSnake, which wasn't valid Python, but running MyPy would generate Python files from the TypeSnake and drop them into your project folder, where you could run them.

@conartist6
Copy link

conartist6 commented Aug 16, 2024

That much seems obvious to us both. Might I repeat the question? Given that we agree that derivate typed languages are not themselves JS, what is the benefit of this proposal to JS?

@orta
Copy link
Collaborator

orta commented Aug 16, 2024

That is what the opening parts of the README for this repo cover: https://github.com/tc39/proposal-type-annotations?tab=readme-ov-file#motivation-unfork-javascript

@dfabulich

This comment was marked as off-topic.

@conartist6
Copy link

conartist6 commented Aug 16, 2024

Again, I see NO benefits described in that section for JS or JS developers.

It claims the benefit will be "unforking" the ecosystem yet the JS ecosystem is not forked.

Thus this proposal effectively does the reverse of what it says on the tin: it takes an ecosystem (whole and undamaged) and forks it. Before this proposal, everyone who writes JS understands the words of the language to mean the same thing. Afterwards that will never be possible again.

@conartist6
Copy link

conartist6 commented Aug 16, 2024

To the extent that the ecosystem is forked it is the ecosystem of type checkers that is forked, but that neither falls under the purview of TC39 nor is it at all fixed by this proposal. It is not any comfort to me that everyone should use the same characters of syntax if they don't agree on what they mean.

@dfabulich

This comment was marked as off-topic.

@lillallol

This comment was marked as abuse.

@conartist6

This comment was marked as off-topic.

@shaedrich
Copy link

To the extent that the ecosystem is forked it is the ecosystem of type checkers that is forked, but that neither falls under the purview of TC39 nor is it at all fixed by this proposal.

The ironic thing is, actual type checking is again outsourced outside TC39 to type checkers that can all have their own implementation. So, essentially, the problem is only moved somewhere else but not actually dealt with inside JavaScript itself.

@ctcpip
Copy link
Member

ctcpip commented Aug 16, 2024

Please be mindful of our Code of Conduct 🙏

@nektro
Copy link

nektro commented Aug 16, 2024

@dfabulich if users want to simply[1] use typescript out of the box they should use bun or deno. this repo is a core ecmascript proposal and thus should be going its own path from first principles without necessarily worrying about the impact on prior art. the unforking comes after when prior art updates their code to use the new standard.

@conartist6
Copy link

conartist6 commented Aug 16, 2024

I am (and have been) working on a counterproposal which could solve the underlying problems. I am proposing a system of arbitrary syntax transformation -- a macro system for JS, if you will. I believe such a solution could have some benefits:

  • It does not require blessing some syntaxes over others
  • It does not broaden the definition of JS, but allows other languages to shrink themselves to fit inside. Instead of reserving some structured syntaxes as undefined, it permits the handling of any arbitrary syntax whenever it can be well-defined
  • It is able to support the desired developer features like source mapping and no-build execution of arbitrary code
  • It is able to cope with the ambiguity that arises in situations when an implementer runs ahead of the spec, resulting in ecosystem forks within particular features such as typescript's pre-approval version(s) of decorators

@aspirisen
Copy link

I am personally think that runtime type checking is impossible due to rich type system in TypeScript. For example take a look on type-fest library which has super complex utility types like this one. If there will be type checker in JS engine then it will have to evaluate and check everything.

Just take a look on how long it takes for TypeScript to check types in big projects and pretty much the same time browser will spend to do the same, of course it will be faster due to native environment, but still it will slow down page speed.

Another question is what to do if the types are incorrect (and maybe the code will work in runtime), should browser through an error and break the page, if not then what will be the point of real type checking?

I think maximum what is possible is some kind of type reflection, or adding the type text to decorators metadata to allow you to parse and use it in runtime (i.e. in schema validation like class-validator does)

@conartist6
Copy link

conartist6 commented Aug 16, 2024

I have read through the arguments about why adding runtime type checks is impossible, and I am forced to say that I concur with them.

Typescript already has a relatively advanced system which allows it to condition or refine its type understandings based on the results of runtime checks. That is already a powerful enough tool to be able to design an ideal system in which untrusted values are checked at the boundaries and internal static analysis can then be more or less "sound" in the type-theoretic sense. Because Typescript has such a system its system of type is able to have well-defined meaning, even in the absence of a compiler which emits any (non-explicit) runtime type checks.

It seems likely that similar mechanism would prove useful to many extenders, allowing them to marry their theory to Javascript's actual (runtime) type system, thus providing the means for consensus to be reached on which constructions are "correct" in their language.

@shaedrich
Copy link

shaedrich commented Aug 16, 2024

I am personally think that runtime type checking is impossible due to rich type system in TypeScript.

Most of us have probably seen what kinds of monstrosities TypeScript is capable of committing when devs go wild and define acid trip like types, which extend themselves to infinity and beyond, resulting in more code for the types than for the actual code, it will result in JavaScript. No wonder why there are sometimes separate .d.ts files. So, the rich type system can be as much an argument for inclusion into JavaScript as it can be on against it. Maybe, this kind of complexity isn't even needed in the first place, especially when it has no meaning in the language itself.

@tabatkins
Copy link

Given that we agree that derivate typed languages are not themselves JS, what is the benefit of this proposal to JS?

As I've said in my earlier comment referencing Python, it brings the same value to JS that Python's type annotations bring to Python.

Specifically:

  1. you can run your annotated code directly, rather than needing a preprocessor (tho for professional usage you will want to run a preprocessor, to strip the types/comments/etc and do other minification/etc)
  2. we're not marrying the type system to specific runtime syntax/semantics that then become subject to the back-compat constraints of live code on the web
  3. we don't impose any runtime costs on users running the code, only checking-time costs on code authors

(2 and 3, of course, apply to many possible solutions, including current TypeScript. 1 is the big difference from current practices. But it's important to keep 2 and 3 in mind, when weighing against proposals like runtime type checking.)

I apologize for apparently overexplaining earlier, but any assertion that this sort of system wouldn't be useful for JS needs to directly grapple with the usefulness of the same system in Python. If you have an argument against it, why does that argument not apply to Python's type annotations?

@conartist6
Copy link

I too want a solution that hits points 1, 2, and 3!

As you mention the existing system hits points 2 and 3, but not 1.

This proposal satisfies 1 and 3, but not 2.

@conartist6
Copy link

conartist6 commented Aug 23, 2024

The difference with Python's system, now that I have a chance to sit down and look more carefully and look at Python's system, is that while Python allows multiple checkers it also defines the meaning of each of its typing syntaxes.

While a given checker is not specified, there is ample documentation in the base language on what the various typing syntaxes (like generics) mean and how you should write types for each kind of code. To me that is very different than how JS would be able to write the same docs, which is to say it wouldn't.

@spenserblack
Copy link

spenserblack commented Aug 23, 2024

Since Python keeps getting mentioned, I think it's worth noting that, while Python doesn't assert type annotations at runtime (x: int = "one" is OK), the type annotations are known at runtime.

from typing import get_type_hints

class Foo:
    bar: str = "bar"

    def __init__(self, baz: bool):
        pass

get_type_hints(Foo)  # {'bar': <class 'str'>}
get_type_hints(Foo.__init__)  # {'baz': <class 'bool'>}

This allows users to use those type annotations at runtime. For example, to perform runtime type checking with the typeguard library.

I think that forced runtime type checking has been discussed plenty, in this issue and others. But it might be worth discussing if types should really be completely ignored, or if they should/could somehow be available at runtime.

@ctcpip ctcpip mentioned this issue Nov 14, 2024
@quantuminformation
Copy link

I am personally think that runtime type checking is impossible due to rich type system in TypeScript.

Most of us have probably seen what kinds of monstrosities TypeScript is capable of committing when devs go wild and define acid trip like types, which extend themselves to infinity and beyond, resulting in more code for the types than for the actual code, it will result in in JavaScript. No wonder why there are sometimes separate .d.ts files. So, the rich type system can be as much an argument for inclusion into JavaScript as it can be on against it. Maybe, this kind of complexity isn't even needed in the first place, especially when it has no meaning in the language itself.

real talk, half the time in Solidjs you are fighting types

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests