tc39 / proposal-type-annotations

ECMAScript proposal for type syntax that is erased - Stage 1
https://tc39.es/proposal-type-annotations/
4.25k stars 46 forks source link

More discussion of comments-as-types other than JSDoc? #41

Open bakkot opened 2 years ago

bakkot commented 2 years ago

The obvious alternative to this proposal is to just use the existing comment syntax for types. The readme's counterargument is that JSDoc comments are verbose and missing some things you'd want to do in typescript. This is true, but not obviously relevant, since JSDoc is not the only syntax possible. As an existence proof, Flow's type comments are quite concise and support (I believe) all of Flow.

So why the focus on JSDoc? The readme currently makes it sound like JSDoc is the only imaginable way to use the existing comment syntax, and concludes that the (true) fact that JSDoc is verbose and unergonomic is therefore a reason that the existing comment syntax is inadequate, but that seems to be a deficiency in JSDoc in particular rather than comments in general.

chrisdothtml commented 2 years ago

+1

Personally I do like using the JSDoc syntax in smaller projects, but have found it falls short for more complex scenarios (e.g. overrides, interfaces). I don't think that makes JSDoc a hopeless cause though; I think supplementing it with flow-like comment support would be better and avoid the permanence of new JS syntax. For example:

Existing syntax still works for simple stuff

/**
 * @param {number}  p1
 * @param {number} p2
 * @return {number}
 */
function add(p1, p2) {
    return p1 + p2;
}

But also works with more complex types by supporting full typescript syntax in block comments

/*::
interface Person {
  name: string;
  age: number;
}
*/

/**
 * @param {Person} person
 * @return {void}
 */
function greetPerson(person) {
  console.log(`Hello, ${person.name}!`);
}

And can be circumvented entirely if needed

/*::
interface Person {
  name: string;
  age: number;
}

type GreetFunction = (person: Person) => void;
*/

/**
 * @type {GreetFunction}
 */
function greetPerson(person) {
  console.log(`Hello, ${person.name}!`);
}
bakkot commented 2 years ago

To expand somewhat:

This proposal has some cost. Being able to run type-annotated code without a build step has some benefit.

We can't objectively assess the magnitudes of costs and benefits, but we can do comparisons based on what people actually use. JSDoc comments achieve (some of) the claimed benefit, but most people aren't currently using JSDoc comments, so we can say that the claimed benefit is not worth the cost of JSDoc comments. That puts an upper bound on the benefit, but it's a pretty high bound, because of the high cost of JSDoc.

But the cost of flow's type comments is (in my experience) much, much lower than JSDoc comments.

If the claim is that people wouldn't use flow-style type comments in TypeScript, that places a much, much lower bound on the benefit of this proposal: they're not very much extra work, so if people aren't willing to use them to get the benefit of avoiding the build step it must be the case that there's not all that much value in avoiding the build step. On the other hand, if people would use them, there's not much additional benefit to adding the syntax to the language - either way you already get the benefit of running type-annotated code without a build step; the only additional benefit would be to save you a few characters per type.

edmundmunday commented 2 years ago

I don't know if it's 100% correct to say that JSDoc comments are "not worth it" - one of the biggest issues with JSDoc comments IMO is the documentation is terrible and the implementation of them for checking in tools like IDE's is not perfectly standardised. That's more of a critique on the JSDoc ecosystem than the concept itself.

I'm speaking as someone who's a strong user of JSDoc type annotations and prefers it over TS due to its ease of use vs TS.

ljharb commented 2 years ago

JSDoc with TS lacks a ton of the features that native TS has; I’m forced to basically define everything in a d.ts file, and use jsdoc import() syntax to pull in definitions, and anything beyond that is quite laborious. That of course is a complaint about a specific wildly underspecified dialect of JSDoc with a specific type system’s checker, and not a complaint about this proposal.

nweldev commented 2 years ago

There is a lot of room for improvement of JSDoc support in TypeScript. Consequently, "JSDoc is bad" should never be considered a valid argument in favour of this proposal, whatever how true it could be.

giltayar commented 2 years ago

@noelmace I agree! I love JSDoc typings, and have been using it for more than a year now. JSDoc typings should be improved, regardless of this proposal.

But this is out of scope for this proposal. The reason for this proposal is not (only and mainly) because JSDoc is inadequate right now, but mostly because "types as an integral part of the syntax" have won the hearts and minds of JavaScript developers, as can be seen by the growing popularity of TypeScript.

bakkot commented 2 years ago

@giltayar The growing popularity of TypeScript is good evidence that types are a thing people want. "Types as an integral part of the syntax" is the least important part of TypeScript.

giltayar commented 2 years ago

@bakkot let's say we want to define a new syntax that is like JSDoc, but nicer. We would have to start from zero, because there is no prior art. It would, if I remember my politics well, take a huge amount of time. And if we standardize it, what chance would it have against an entrenched type system like TypeScript? Maybe it will "win", but I believe people will keep transpiling TypeScript, because that is now what everyone uses.

This proposal goes with existing art. It takes what has been proving to be acceptable to the community, and tries to standardize that (while leaving a place for more type system improvements). I believe this is the fastest and best way to evolve JavaScript to have types.

bakkot commented 2 years ago

We would have to start from zero, because there is no prior art.

Sure there is.

And if we standardize it, what chance would it have against an entrenched type system like TypeScript?

This proposal does not include a type system at all. And the syntax is basically completely divorced from the type system; a proposal for a different syntax would not mean using a different type system.

I believe this is the fastest and best way to evolve JavaScript to have types.

This proposal does not bring types to JavaScript.

wparad commented 2 years ago

The sentiment in this issue sounds a bit like "We can't do the right thing because it would be hard" and even more so, "typescript won, we should figure out a way to pull typescript types into javascript". Why can't we start with focusing on the value for javascript as a language and how to make that happen. If we have opportunities to reuse existing things once we get there, let's do it, but I would really like to avoid the current approach.

Is it hard to do the right thing, it seems even harder to do the wrong thing because a lot of people don't want that. If types are missing from javascript, which is the image that keeps being passed around, why cannot we just not start introducing some types as first-class citizens to the language. Where is the difficulty there?

giltayar commented 2 years ago

why cannot we just not start introducing some types as first-class citizens to the language. Where is the difficulty there?

My 2 cents: because you'd need to do it incrementally. A big bang type system that comes all big will probably never pass TC39. And if you do it incrementally, it could probably take 10 or more years.

In the end, the Web is a pragmatic medium. HTML, CSS, and JS are not "the perfect tools" and became what they are through pragmatic evolution. This proposal continues this line of pragmatic evolution.

wparad commented 2 years ago

Let's open the door for the incremental approach, it won't take 10 years, it will deliver the most important feature. Agile > Waterfall > Hacks > opinions about how long it takes to do something.

This isn't a pragmatic solution, if we want a pragmatic solution, we would be asking "What is the most important feature of types that people are actually asking for" And start by knowing what that is, and then implementing it. I don't think it makes sense to spread fear about trying to implement a type system or doubt our ability to do so. Let's do it incrementally, and then no one would be against it.

ptomato commented 2 years ago

I always find the discussion is heading in the wrong direction when people start referring to the other person as "spreading FUD" in an opinion-based disagreement ... could we not, please?

Jack-Works commented 2 years ago

let's say we want to define a new syntax that is like JSDoc, but nicer.

Yeah, just like @bakkot suggested in the matrix.

/*::
type Point = {
    x: number
    y: number
}
*/

//:: (number, number) => Point
function f(x, y) {
    //:: Point
    const p = { x, y }
    return p
}

why cannot we just not start introducing some types as first-class citizens to the language. Where is the difficulty there?

Because there is an impossible triangle in this problem:

  1. To have a type system that actually catches bugs
  2. Web Compatibility
  3. Committee don't want to introduce breaking change by versioning in JavaScript ("use ES2025")

If the type system needs to be useful in the long term, it should reject invalid programs (in the type checking semantics). To achieve this, you need one of the...

lillallol commented 2 years ago

@ljharb

JSDoc with TS lacks a ton of the features that native TS has

Here is one: enum, (although I think it lacks due to support). Can you give some more examples please?

I’m forced to basically define everything in a d.ts file

Why not a .ts file?

and anything beyond that is quite laborious.

Can you give an example that belongs to that?

ljharb commented 2 years ago

@lillallol more examples: declaring package types, and i believe a bunch of type definition forms - it's been awhile. why not a .ts file, because i want my code to be javascript - i'm only trying to author the types. "laborious" refers to typecasts, or places where i need to declare the type of an individual variable, etc. It's been awhile since I tried, to be fair, but the prevailing attitude i found was "just use typescript" whenever someone had a jsdoc complaint.

lillallol commented 2 years ago

@ljharb

declaring package types

I do not fully understand what you want to say here. I am not against .d.ts for enabling types for legacy libraries (or even wanting to make tsserver faster). TS has introduced maxNodeModuleJsDepth so if you write something via JSDoc + TS there is no need for separate package types, right?

and i believe a bunch of type definition forms

I do not understand what you mean. Can you please give an example.

"laborious" refers to typecasts, or places where i need to declare the type of an individual variable, etc.

You mean this : /**@type {import("./from/some/path").IMyType}*/ ? You can enable an IDE shortcut that will give : /**@type {<cursor will be here and you have auto import intellisense>}*/. So one shortcut key + some keys enough to trigger autoimport .

but the prevailing attitude i found was "just use typescript" whenever someone had a jsdoc complaint.

Yeah, unfortunately : that's how things are done around here.

Have you personally ever a made a project by importing .ts types via JSDoc comments in .js files? Did you got stuck somewhere?

ljharb commented 2 years ago

@lillallol yes, i've tried on multiple projects to do this. I initially wanted to solely use jsdoc, and no .ts files or d.ts files at all - but I quickly found JSDoc support in TS to be wildly insufficient, so then I moved to a .d.ts file with jsdoc imports. Most of the places i've gotten stuck at that point are bugs in TS, or, design decisions that render it unable to accurately describe JS semantics.

lillallol commented 2 years ago

Most of the places i've gotten stuck at that point are bugs in TS

Can I please have an example?

design decisions that render it unable to accurately describe JS semantics.

Same here, can I please have an example?

ljharb commented 2 years ago

@lillallol sure, here's two: https://www.typescriptlang.org/play?#code/MYewdgzgLgBAHjAvDAzAbgFCkrAnkmMAVwBsTNtoYAvAgbQBYBdTLcKgQwCcv6mA6bMA5QAFHAA0MfAB8ZMOkynUAlGhgB6DTCi4ADgFMYASwgwARGAMA3A10XmYAIyKwIACxCkAJs6OWiAFsnOwcMcLYcGBACAG8YAH0EvS4QKBAkgC5CUhIYAF9MEH50gGUoLmMwAHNRNU1tDy8SXxCYDhgAFX0DAFEeEC4gA

If you have any similar questions in the future, find me almost anywhere else - this repo/issue isn't the proper place to be discussing specific issues with individual typed JS alternatives.