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

Types as comments without changing JavaScript syntax. #176

Open
azder opened this issue May 12, 2023 · 52 comments
Open

Types as comments without changing JavaScript syntax. #176

azder opened this issue May 12, 2023 · 52 comments

Comments

@azder
Copy link

azder commented May 12, 2023

You can have it today, for not extra price and for a lot cleaner code.

Types as comments should work similar to how JSDoc doesn’t need to change the syntax of JS and how Haskell doesn’t need to mix type declaration with the definition, but adds it just before it, with good compilers if you really need it since inferencing will do most of the work.

This is comments as types

    // hello :: string => string
    
    const hello = name => ‘hi ‘ + name ;

You can change the comment syntax a bit, like

    //: hello = string => string

or

    // type hello = string => string

or whatever else you like.

Just keep it clean and simple please, and most importantly, out of the definitions we already have in the language. Type information can just be added besides them

@Abdelaziz18003
Copy link

Abdelaziz18003 commented May 14, 2023

Normally what you've described above can be solved with the already existing JSDoc syntax.

    /** @type {(hello: string) => string} */
    function greet(name) {
        return ‘hi  + name ;
    }
     
    /** @type {number[]} */
    const arrayOfNumbers = [];

@GrantGryczan
Copy link

GrantGryczan commented May 16, 2023

I think you may have missed the purpose of this proposal? What you're describing already exists.

This proposal isn't about adding type annotations to JavaScript; it's about not needing an extra compile step in the existing typed variations of JavaScript, and about not having to use JSDoc comments which are generally more tedious to read and write (and are thus less clean and simple).

@azder
Copy link
Author

azder commented Jun 28, 2023

What you're describing already exists

... in a sad state. Can't remember when it was last updated and it still has the syntax of a JavaDoc that was there as a complement to an existing static type system.

Or, if it exists, can't it also be an argument against this entire proposal? "JSDoc exists, we don't need this proposal" kind of thing?
The answer will again be "in a sad state".

Would be nice to have something more powerful, but away from the existing JS syntax. A clean separation is even easier on the eyes, less clutter in that one line where you define a function, its type and what else... annotations one day?

JSDoc are tedious because their syntax is not that good, and hasn't been updated much these last years, so it has nothing to do with them being separate from the actual function.

@azder
Copy link
Author

azder commented Jun 28, 2023

Oh, I forgot

it's about not needing an extra compile step in the existing typed variations of JavaScript

What I suggested is in line with this above, you can have your single compile step read the comments AND code, right? We can add that to the language itself, be it a // comment, or a type keyword that introduces it, separate from the existing syntax.

This proposal isn't about adding type annotations to JavaScript;

Yes, it is, hence my comment about not doing it as proposed, but a bit different, like I originally posted:

similar to how JSDoc doesn’t need to change the syntax of JS and how Haskell doesn’t need to mix type declaration with the definition,

Just to be clear, I don't consider Flow or TypeScript as JavaScript. They're separeate languages, thus, there aren't "existing typed variations of JavaScript", there are only two variations: strict mode and non-strict mode.

@GrantGryczan
Copy link

GrantGryczan commented Jun 29, 2023

Yes, it is, hence my comment about not doing it as proposed, but a bit different

I think this is the source of your confusion: When I said this proposal isn't about adding type annotations to JS, I meant this proposal's benefit isn't merely that JS gets type annotations. The proposed benefits I referred to were TS syntax not requiring a compile step, and people who prefer TS syntax over JSDoc being able to use that with less friction. Your suggested alternative seems to miss the point because it doesn't substitute a solution for these problems of friction. Instead, it substitutes the addition of type annotations, despite that JS missing type annotations isn't the underlying problem this proposal is trying to solve.

... in a sad state.

No, in an identical state. Your suggestion is just to use comments, which doesn't need a new ECMAScript proposal in order to be valid JS, because it's already valid JS. This proposal is for changing the language and its syntax. If you just want to use existing JS syntax, then you can do that today, and this proposal is not for you. As I already said, this proposal is for people who want convenient type annotations supported by their IDE while avoiding using JSDoc or adding a compile step to their JS project.

Or, if it exists, can't it also be an argument against this entire proposal?

I don't think you've made any such argument? An argument against this proposal would involve justifying that the detriment of this feature outweighs the benefits it provides, such as removing a compile step for type annotations in some situations. I'd understand if you were arguing benefits like that are outweighed, but that doesn't seem to be the case.

@ljharb
Copy link
Member

ljharb commented Jun 29, 2023

@GrantGryczan it would still require a compile step unless the entirety of TypeScript was contained in the proposal, which isn’t the plan as i understand it.

@orta
Copy link
Collaborator

orta commented Jun 29, 2023

I think what would be likely to happen is that there would be a use "TS in JS" flag for projects using typescript checker on JS files with this proposal, the editor would ensure you are writing files which conform to the spec and like with JSDoc support new TS syntax features might take a bit of coercing to fit that space.

Overall though, I'd expect the amount of TS-in-JS files to eat into "plain 'ole .ts" files/projects to the point where most people are writing the TS-in-JS and that the lack of a compile step is a strong pull for that

@spenserblack
Copy link

I think what would be likely to happen is that there would be a use "TS in JS" flag for projects using typescript checker

If this proposal is accepted, I wonder how all the tools in the JS ecosystem would change as a result. For example some projects may not have typescript installed as a dev dependency, using type annotations to communicate intent, but not for type checking. Other tools may be updated or then created to ensure no forbidden syntax like enum or namespace. I suppose that syntax errors would be good enough, but some may want a simple check saying "whoops, looks like you used a Typescript feature in JS" without all of the type checking.

🤔 Also, I wonder if some typescript-eslint rules would make their way to eslint as a result.

@orta
Copy link
Collaborator

orta commented Jun 29, 2023

Any JavaScript tool like eslint would know how to skip over the code which defines types, as defining that behavior would be a part of the spec. It would allow the same tools to work for more than just TypeScript for example, as other type system can fit into the space

If you have code like namespace and enum though, which does not exist in TS-in-JS (unless added by TC39 in the future) your tools would correctly throw and you could hardcode error messaging to say that this is a TypeScript file and should really have a .ts extension

@GrantGryczan
Copy link

GrantGryczan commented Jun 30, 2023

it would still require a compile step unless the entirety of TypeScript was contained in the proposal, which isn’t the plan as i understand it.

@ljharb I didn't mean using TS features this proposal doesn't support. For people only using supported features (which most of the features are), a compile step would be eliminated. Or, to put it differently, for people using vanilla JS (especially Node.js where there often isn't already a compile step for minification) who would benefit from type annotation, it would make introducing types to their codebase more accessible due to the reduced friction, especially for those who prefer TS syntax over JSDoc (which, judging by their popularity, is most people).

Also, I'm not sure why you're saying a compile step would necessarily be required, considering reducing the need for a compile step is brought up by the proposal itself multiple times? That's why I commented about it in the first place. To quote it (regarding two different use cases):

This proposal will reduce the need to have a build step which can make some development set-ups much simpler. Users can simply run the code they wrote.

Node.js developers in particular, have historically avoided transpilation, and are today torn between the ease of development that is brought by no transpilation, and the ease of development that languages like TypeScript bring.

@ljharb
Copy link
Member

ljharb commented Jun 30, 2023

I don’t personally think there’s much value in making things more complex by saying that some, but not all, TS users can avoid transpiling.

@GrantGryczan
Copy link

GrantGryczan commented Jun 30, 2023

I'm not here to argue whether there's value in it, I'm just clarifying one of the points the proposal makes since this issue doesn't seem to attempt to solve the same problems the proposal is attempting to solve, or at least attempt to solve some of the problems with a better trade-off. This issue only suggests a new format for type annotations as comments--so not something that could be a TC39 proposal since comments are already valid JS, just as JSDoc also didn't require a TC39 proposal.

@lillallol
Copy link

not something that could be a TC39 proposal since comments are already valid JS

Correct me if I am wrong, but the scope of tc39 is[link]:

Scope:

Standardization of the general purpose, cross platform, vendor-neutral
programming language ECMAScript®. This includes the language syntax, semantics,
and libraries and complementary technologies that support the language.

Static typing is a complementary technology that supports the EcmaScript language. A static type system needs specification to be defined, regardless of whether it reserves syntax inside native comments.

I would like the opinion of @ljharb on this one.

@GrantGryczan
Copy link

GrantGryczan commented Jul 2, 2023

I hadn't personally seen any instance of TC39 handling anything besides ECMAScript semantics and syntax, so I was under the impression this didn't fall under its scope, but if it actually does, then that's reasonable! I thought it would be something the community would be better off handling like TypeScript, Flow, JSDoc, etc.

But either way, I still don't think this suggestion attempts to solve the same problems this proposal does. You may disregard the sentence of my previous comment about TC39's scope.

@lillallol
Copy link

But either way, I still don't think this suggestion attempts to solve the same problems this proposal does.

Be more specific please.

@GrantGryczan
Copy link

GrantGryczan commented Jul 2, 2023

I've already argued that statement specifically in most of my previous comments, so I don't think continuing to repeat myself or participate in this conversation will really help anyone. If my first few comments didn't get my point across, it doesn't seem any further comments will (at least in a reasonable amount of time). Sorry to end the discussion abruptly.

@lillallol
Copy link

For people only using supported features (which most of the features are), a compile step would be eliminated
it would make introducing types to their codebase more accessible due to the reduced friction, especially for those who prefer TS syntax over JSDoc.

Please read the tc39 meeting notes [1][2][3][4]. The context proposal has yet to provide a syntax that is working. The assumption that the context proposal will support, all or some of the already existing TS syntax, has no proof. Also there are no restrictions to the solution scope in stage 1 proposals. What the op suggests, and any other suggestion that does not need to introduce new "comments", solves the problem statement, and despite how much people do not want to admit it, this is done without the drawbacks of the context proposal. To make matters worse the problem statement is already solved without the need for any kind of tc39 proposal. Take a look for example how SvelteKit and Deno used TypeScript without the need to compile:

Have you actually created projects like this? I personally have. Most people have not, and that makes them believe that we need the context proposal.

Finally this:

friction

mentioned in tc39 meeting notes, is nothing more than an artificial problem created by the intrinsic drawbacks of supersets: arbitrarily reserving syntax without tc39 approval. You simply stop using supersets, and move to complements, and the problem is solved. It is my opinion that tc39 should strive for people to stop using supersets, since this will lead to a healthier ecosystem.

In the end the fact that people seem to not really know what is going on with the context proposal, hence countless issues like this context, is because, the maintainers of the context repo do not seem to be bothered to just add links to the tc39 meeting notes in top of the README.md file.

@azder
Copy link
Author

azder commented Jul 2, 2023

@GrantGryczan there is no confusion. You're proposing syntactic change to one language for the benefit of another language. TC39 should change syntax for the purpose of the EcmaScript language. That is all.

My suggestion was to evolve the ECMAScript language (or at least the practice of it) in a way that:

  • doesn't require syntactic change to have benefits
  • provides for separation of concerns and cleaner coding practices
  • give people who like to play with static types a way to do it
  • doesn't care about other languages (like TS or Flow) since they're outside the scope of EcmaScript
  • maybe the comment syntax evolves in a more "modern" way (JSDoc is stuck in the "middle ages" atm)

I think @lillallol threaded the similar line with the problems of supersets, and what I consider "muddying the waters" by considering other languages as JS-but-* (better, different, etc). They aren't it, so any proposal for a complementary technology should be part of a complementary technology technical committee, and any proposal to TC39 should be about the benefit to EcmaScript, not other languages.

@spenserblack
Copy link

Take a look for example how SvelteKit and Deno used TypeScript without the need to compile:

You've used these projects a few times as examples for why you don't need Typescript or typescript-like type annotations. Have the maintainers weighed in on this proposal anywhere? Especially for SvelteKit, where they seemed most interested in removing a compile step, I wonder if they considered JSDoc to be the best, or the best currently available, and if they would have considered some of the proposed syntax of this proposal to be a better choice.

@lillallol
Copy link

You've used these projects a few times as examples

To be honest I use these projects as examples because:

  • everyone knows them
  • they are complex projects
  • I have not uploaded my projects on github

I do not need them as a proof for what I claim. My projects are enough.

Have the maintainers weighed in on this proposal anywhere?

As far as I know, no. Why are you making this question? That is a strange question. It seems to me, like a question that would come from someone who have not used TypeScript the way these projects do. Have you actually used TypeScript like these projects do? There is no going back when you are appropriately exposed to this way. It is simply better.

for why you don't need Typescript or typescript-like type annotations.

Lets be clear here. TypeScript type checking is needed. I would never use JSDoc on its own without TypeScript. I personally use TypeScript in all of my projects regardless the size. I just do not use TypeScript as a superset. I write strictly all types in .ts files and import-type-annotate variables in .js files with this /**@type {import("./path/to/file.js").IMyType}*/.

if they would have considered some of the proposed syntax of this proposal to be a better choice.

Native supersets are currently not working. There is no working syntax provided by the context proposal, hence it makes no sense to ask them whether they have considered it, since we might end up with a completely different syntax from the current one.

For the case a working syntax is found that is accepted by all of the tc39 members (when? next year? next decade?), then all the pros and cons should be considered. For example it makes no sense to just compare syntax when breaking changes (like nothing of TypeScript is valid syntax) are introduced to all supersets + and the JS parser becomes slower regardless of the usage of the new syntax.

@spenserblack
Copy link

@lillallol TBH I'm not going to do much back and forth and respond to each piece, as I feel like anything further would bloat the comments without adding much value.

But to respond to you asking why I was curious about the opinion of those projects' maintainers:

I assumed you were using those projects as proof that your arguments are valid. With that assumption, it seemed to me that the opinion of maintainers of these large, popular projects would be valuable, and that the continued usage of them as proof would be unfairly putting words in their mouths.

Also, your success with using declarations and importing them with JSDoc comments in private projects might be proof enough to you, but I'm sure you can understand why it might not be convincing to me or others you've debated with.

You are correct that I haven't used types in this way. But for smaller projects, I have actually considered dropping TS and writing JS with declaration files, because, as I've said in a few other places, I'm more interested in using types to communicate intent than to be strictly asserted. So perhaps I will someday. This may not be the most compelling reason, but I do find a TS-like (or Python-like) type annotation to be the easiest way to write intended types, hence my interest in this proposal.

@lillallol
Copy link

but I'm sure you can understand why it might not be convincing to me or others you've debated with.

I do not understand. Be more specific please.

@egasimus
Copy link

egasimus commented Sep 5, 2023

There is no going back when you are appropriately exposed to this way. It is simply better.

Hard disagree about the "simply better" part. Am "appropriately exposed" to TypeScript. It sucks and is user-hostile. Fight me.

The "no going back"... jury's still out on this one. Either there is a going back (to JavaScript as a scripting language, without bundlers, preprocessors, compilers being a mandatory part of the toolchain), or, thanks to WASM, there's a going to a different language 🤷

@msadeqhe
Copy link

msadeqhe commented Sep 23, 2023

You can have it today, for not extra price and for a lot cleaner code.

Types as comments should work similar to how JSDoc doesn’t need to change the syntax of JS and how Haskell doesn’t need to mix type declaration with the definition, but adds it just before it, with good compilers if you really need it since inferencing will do most of the work.

JavaScript always had interface and implements keywords, that means every language has to evolve someday, and JavaScript had this goal from the ground up.

@azder
Copy link
Author

azder commented Sep 23, 2023

every language has to evolve someday, and JavaScript had this goal from the ground up

That's not in question.

The thing is, how? There are many questions connected to it:

  • by copying verbatim what has been piled up over time by inertia and sedimentation in other languages?
  • how long would one have wait for it to be implemented and supported before it's useful for many?
  • what does it mean if you standardize one syntax and leave it difficult for other type systems to be added later?

With comments, you can have flexibility and freedom to extend with different solutions without spending years disussing it in a technical comitee that has to be concerned that what you add to the standard is hard to remove least you break the Web.

Otherwise, here is one version that doesn't require you to re-invent syntax so you will be able to shoehorn it in every place imaginable:

  • reusing existing type systems, not re-inventing the wheel, allowing for expansion
// blocks of interfaces can have identifying tokens in order to support multiple syntaxes and versions

interface ('TypeScript','5.2.2') {

    type add = (a: number, b: number) => number;
    type Sub = (a: number, b: number) => number;
    
    declare function plus <T extends number|string>(a:T,b:T): string;
}

interface ('Flow','0.217.0') {
  
    type add = (a: number, b: number) => number;
    type Sub = (a: number, b: number) => number;
  
    declare function plus<T:number|string>(a: T, b:T): string;
}
  • importing from other files, instead of keeping all in one place, for optimization
// the file itself can have the identfying tokens
interface "./local/interface/definitions/file.ijs";

// or add them inline
interface ('TypeScript','4','d.ts') from "./local/interface/definitions/file.v4.d.ts";
interface ('TypeScript','5') from "./local/interface/definitions/file.v5.tjs";
interface ('Flow') from "./local/interface/definitions/file.fjs";

// same identifier as declared in the above types
const add = (a, b) => a + b;

  • and now the "shoehorn" part, how much of the following is too much?
// forcing the type manually
const sub interface Sub = (a, b) => a - b;

// but limited syntax to only the type identifier after the keyword, like
const plus interface Plus = (a,b) => a + b;

// nothing extra?
// could it be specific to one or two type systems/checkers, and limiting the rest?
const plus interface Plus<number> = (a,b) => a + b; // is generics here too much?

// allowed only in `const`, `let`, `var` syntax i.e.
const identifier interface Type = ...

// but not in expressions and definitions like the following?
const result = someFunction( a interface Type ); 

// when does it become too intrusive?
function otherFunction(a,b) interface OtherFunction {
    return a + b;
}

This last block (the shoehorned part) is contrary to the separation of concerns I would like to have and to think will be useful as to allow more freedom for newer and better ways to express and check the types

@egasimus
Copy link

egasimus commented Sep 23, 2023

@azder This kind of dovetails with a thought I had when discussing namespace with @unional on #167. Maybe the package keyword would be a good fit for the first couple of examples?

Even in combination: interface package, export interface package. These have a certain pronunciability to them as well.

Example (not with types but it would work the same):

If currently you can do:

// impl.js
export function foo (a, b) { return a + b }
export const bar = 1
// index.js
import * as Impl from './impl.js'
import * as assert from 'node:assert'

assert(Impl instanceof Module) // import "returns" Module

export function fooBar (baz) {
  return Impl.foo(Impl.bar, baz)
}

The package keyword could make it writable as single file:

import * as assert from 'node:assert'

assert(Impl instanceof Module) // hoisting like functions

export function fooBar (baz) {
  return Impl.foo(Impl.bar, baz)
}

// create Module in place, automaticalyl enclosing a namespace
package Impl {
  // parent scope is not visible here
  export function foo (a, b) { return a + b }
  export const bar = 1
}

This would do native namespacing for both types and values.

And then it could also be used to denote code that is governed by an external compiler, e.g.

import * as assert from 'node:assert'

assert(Impl instanceof Module)

export function fooBar (baz) {
  return Impl.foo(Impl.BAR, baz)
}

package Impl {
  export const BAR = 1
  export function foo (a, b) { return a + b }
}

export interface package ("typescript@5.2.2") {
  export function fooBar (baz: number)
}

@unional
Copy link

unional commented Sep 23, 2023

if (!(Impl instanceof Module))

This scares me 🤣. The result of import * as Impl should always be that case and needing to do any check at runtime will be very problematic.

Not sure about the rest of the discussion, need to read through. 🌷

@azder
Copy link
Author

azder commented Sep 24, 2023

@trusktr please note, I don't use //: as a way to say a) "this next variable is of type..." or b) "this preceding argument is of type..." but as an escape sequence like //** is.

It's only meant to signal your pre-processor that it's not a regular comment, but something your DSL can work with. So basically //: A = string is a stand in for type A = string as found in Flow or TS.

Which is why in this example #192 (comment) I used //:~ to denote the use case similar to a)

@theScottyJam
Copy link

Just to clarify intent, when you have something like this:

interface ('TypeScript','5.2.2') {

    type add = (a: number, b: number) => number;
    type Sub = (a: number, b: number) => number;
    
    declare function plus <T extends number|string>(a:T,b:T): string;
}

How exactly is this being parsed?

Is the type-system tag ('TypeScript','5.2.2') being ignored like it was a comment? This is just metadata for the type checker to pick up and use?

What about the body of the interface. Is this behaving:

  1. Like a token soup, where everything inside still gets tokenized using JavaScript's tokenizer, but then everything gets ignored (including matching curly brackets found inside)?
  2. Like a block comment, except with perhaps minimal bracket matching so you have have nested curly brackets getting ignored?

Option 1 is similar to what the proposal currently does in some places, and it means this:

// Valid
// The inner curly brackets match up and get ignored.
// The `{` inside the string is not treated as an inner curly bracket
// since we have enough context to understand that that's
// supposed to be thought of as a string.
interface ('TypeScript','5.2.2') {
  type ObjType = {
    readonly value: '{'
  };
}

// Invalid
// there's not a closing quote to match the opening quote
// Not all languages things of single quotes as strings,
// e.g. the ML family of languages allow you to put
// one single quote next to a type name.
interface ('TypeScript','5.2.2') {
  type ObjType = {
    readonly value: 'a
  }
}

Option 2:

// Invalid
// There are more opening brackets than
// closing brackets, so it's going to try
// to parse whatever comes after this
// as part of the comment as well.
interface ('TypeScript','5.2.2') {
  type ObjType = {
    readonly value: '{'
  };
}

// Valid
// Since we're night trying to tokenize the contents,
// we can invent new uses for single quotes and what-not
// beyond "a single quote is a string".
interface ('TypeScript','5.2.2') {
  type ObjType = {
    readonly value: 'a
  }
}

@azder
Copy link
Author

azder commented Sep 24, 2023

@theScottyJam First, the very simlpe short answers:

  1. How exactly is it being parsed? It isn't, ideally.
  2. Where everything inside... Is maybe just being ignored by JS
  3. Matching the {} makes sense, except it should not be a JS error, so possibly handled like ASI

The following should not cause JS to throw an error, but JS could backtrack and pick the final } as the token for the closing of the interface block.

interface ('TypeScript','5.2.2') {
 // whatever valid or invalid here as determined by the type checking DSL parser
}

In a happy scenario, JS would be ignorant of anything inside the block, skip it, not parse it at all, which is why I mostly prefer using comments, so as to not be changing the language syntax, and especially semantics.

The original comment with that specific syntax was more like a spur of the moment exploration. Further down I've reconsidered, maybe re-using the existing import-from-assert syntax is good enough to get things going, at least peacemeal.

import ts4 from './local/interface/definitions/file.v4.d.ts' assert { types:  ['TypeScript','4','d.ts']  };

The extra tokens are supposed to serve whichever parser is dealing with whichever DSL is being imported. As I've imagined someone might figure out how to use ServiceWorker and WebWorker to provide load time and run time checks, maybe by the environment (like Node) or with a 3rd party parser sent by the server or a browser plugin...

This way people can have all the extra semantics they need, but also not baking it into JS, thus leaving the language as a common denominator for different type checking DSL's without JS being expanded.

And if serving files in this manner is a good start, maybe there will be a possibility of using a reserved keyword. At the very least, comments like //: pre-lined instead of inlined, could be used to test the practicality of next steps. I would imagine the workers can catch those as well from the JS file itself, provided a bundler is smart enough to not erase //: comments or mangle only the identifiers as to cause a mismatch with the type declaration.

@trusktr
Copy link

trusktr commented Sep 25, 2023

@trusktr please note, I don't use //: as a way to say a) "this next variable is of type..." or b) "this preceding argument is of type..." but as an escape sequence like //** is.

It's only meant to signal your pre-processor that it's not a regular comment, but something your DSL can work with. So basically //: A = string is a stand in for type A = string as found in Flow or TS.

Which is why in this example #192 (comment) I used //:~ to denote the use case similar to a)

For sure. Yeah in my examples I was using it as a type for the thing the comment is associated with, rather than definitions. It needs bike shedding

@azder
Copy link
Author

azder commented Sep 26, 2023

the thing the comment is associated with

And that's precisely what I want to avoid. That makes these kind of comments context sensitive and forces (as in soft power) or enables (as in abusive, not empowering) people to mix declaration and implementation in the same place.

@eldoy
Copy link

eldoy commented Jan 26, 2024

If we could just agree on a format, then all editors can support this. Typescript needs to die asap.

@trusktr
Copy link

trusktr commented Jul 16, 2024

mix declaration and implementation in the same place.

That's what I prefer personally though. I like TypeScript's inline annotations, and personally I would like comments to be similar.

The best solution would allow both forms, so people can choose.

@spenserblack
Copy link

spenserblack commented Jul 17, 2024

mix declaration and implementation in the same place.

That's what I prefer personally though. I like TypeScript's inline annotations

As someone who often uses Python, Python's ability to easily "throw in" a type inline has been a lifesaver for improving the editor's suggestions. For example, Django's ORM adds dynamically named methods and attributes, which a type checker can be unable to follow.

For example:

class Foo(models.Model):
    # this FK adds the `foo_set` attribute to MyModel
    my_model = models.ForeignKey("MyModel", on_delete=models.CASCADE)


# foo has an Any type because foo_set is a dynamically named attribute and not
# part of the class definition. Because of this we don't get good suggestions
# from the editor.
foo = my_model_instance.foo_set.get(pk=1)

# now we get appropriate suggestions.
foo: Foo = my_model_instance.foo_set.get(pk=1)

I believe that, for JS, it can be just as helpful to be able to add an inline type with the implementation. I can currently add in a type with JSDoc comments, but honestly (I might sound pretty lazy for saying this) writing out /** @type ... */ feels overcomplicated compared to a simple : ....

Edit: Also, being able to write out types inline with the implementation makes it easy to write "incomplete" type definitions without repeating oneself, where you don't need every type to be defined.

For example:

// I, the writer, don't really care about what type `maybeNull` is,
// and the return type (void) can be derived from the function body,
// so it can be cumbersome to write out more than is necessary for documentation
// or helpful editor suggestions.
function assertIsNull(maybeNull, message: string) {
  // ...
}

@azder
Copy link
Author

azder commented Jul 17, 2024

You missed to quote the important part @trusktr @spenserblack

enables (as in abusive, not empowering)

If you give people the ability, they will abuse it. Some examples of giving giving people the choice in the past caused fights (more like waste time and energy):

  • tabs vs spaces
  • braces placement

This second one is particularly dangerous in JS because people will treat it as a stylistic choice in a language that does ASI and can alter the meaning of the code (that one example of return).

Everything that can be solved with an inline comment could be solved with a comment in the previous line.

@spenserblack as someone coming from the viewpoint of Python, I think you'd appreciate the principle of having only one way to do something.

@trusktr I am usually all for having more options (as I have shown above trying to figure out different extensions to types for JS), but in this particular case, I'm sure if you give people the possibility to write something like TS, they will write it like TS - all in one place, no separation of concerns, no separation of domains (code vs meta-code), even though in TS they also have the ability to separate much of the types outside

type Fn  = (a:number, b:number) => number;

const fn: Fn = (a,b) => a + b;

and yet, people will often do

function fn(a:number, b:number) :number {
    return a + b;
}

because it's how they are used to i.e. "this is how I like it"

@imcotton
Copy link

TypeScript the project is pretty much died at this point; Deno, Bun, Node.js (--loader), esbuild, swc, oxc, etc..., more to come to digest the pie.

User should not cares about types, if JSDoc and LSP (Linter, Checker) serves better role, plain JavaScript would just be fine.

In Haskell you write ONE line annotation on top and it derives into dozens of solid sound code, you could even skip the annotation and just ask the compiler: What type should I put there?

What's the point of reinvent TS-- here? Thanks for all the fish btw.

@imcotton
Copy link

const num = 42;          // this is javascript

const num: number = 42;  // this is typescript day 1

const num: 42 = 42;      // this is typescript day 2

const num = 42 as const; // this is typescript day 3


// this is typescript today, can you spot the difference between javascript?

const num = 42;

@sakgoyal
Copy link

@imcotton what are you even trying to say here?

@imcotton
Copy link

As TypeScript continues to evolve, its ability to infer types without explicit annotations also grows with each release. At its peak, TypeScript essentially resembles JavaScript, free from type annotations.

const repeat = n => 'foobar'.repeat(n);

Currently, you must specify label (n: number) => string for the code above. However, in the future, advancements in TypeScript releases or AI assistants like LLM/Copilot may allow you to omit these annotations, reducing verbosity in your coding activities.

What JavaScript needs in terms of typing are additional data primitives like decimal numbers, float16 etc.., which could benefit directly from engine/VM optimizations at runtime.

Not this:

@unional
Copy link

unional commented Jul 21, 2024

Not exactly. TypeScript can never infer the exact type you want 100% of the time. Not matter how advanced it can become or with the help of AI.

It is physically impossible to do that.
The reason lies in category theory. Type operates at a higher dimension than value. There is no way to infer backward in a 1-to-n mapping.

@imcotton
Copy link

Focusing solely on improving type inference seems too demanding. Personally, I'm not opposed to shifting heavy lifting into .d.ts files as necessary at all; they act like advanced "TSDoc" but in separate files. For simpler cases, relying just on JSDoc works fine.

TypeScript itself still lacks essential type features like curried generics and HKTs, I wouldn't expect 100% accuracy anytime soon, let alone any proposal lacking specificity and capability may as well not achieve this goal too.

Even if such a proposal were adopted in the future, the benefits might not justify the effort, given TypeScript's current capabilities and the future development, which have grown tremendously over the years.

It would be disappointing if, in the future, TypeScript users effortlessly wrote inference-heavy code like JavaScript, while those using this proposal struggled with verbose and less expressive syntax (also not JavaScript).

Overall, while the proposal has its strengths, it may not fully meet market demands.

To echo from beginning of this issue, even as alternative, adopting Haskell's Hindley-Milner type system could offer a robust solution. It promises compatibility with existing code (only annotations in comment), reduces TypeScript's technical debt, and leverages Haskell's proven type system, might also including dependent types (one can dream), what else do we need?

@shaedrich
Copy link

Currently, you must specify label (n: number) => string for the code above. However, in the future, advancements in TypeScript releases or AI assistants like LLM/Copilot may allow you to omit these annotations, reducing verbosity in your coding activities.

I guess, AI/LLM is out of scope here to make decisions based on them, as they are not a native part of the JavaScript ecosystem and shouldn't become one. It's separate technology and everyone can choose to use or not to use it, so it doesn't make sense to include them in this discussion.

@sakgoyal
Copy link

What JavaScript needs in terms of typing are additional data primitives like decimal numbers, float16 etc.., which could benefit directly from engine/VM optimizations at runtime.

you're asking for static types. that is fundamentally NOT what javascript is. JS is inherently dynamic and changing that would require a different language all together. the closes you will get with this is the static hermes project at FB.

this proposal is simply to get TS syntax to be compatible with JS so that transpilation is no longer required. browsers and servers should be able to operate on TS directly without having to strip out types before running the code. This is more in line with the ts-node project

@imcotton
Copy link

I guess, AI/LLM is out of scope here to make decisions based on them, as they are not a native part of the JavaScript ecosystem and shouldn't become one.

No, the case were specify selected because it is part of ECMAScript spec1.

From es2015.core.d.ts2:

interface String {
    repeat(count: number): string;
}

Footnotes

  1. https://tc39.es/ecma262/multipage/text-processing.html#sec-string.prototype.repeat

  2. https://github.com/microsoft/TypeScript/blob/v5.5.3/src/lib/es2015.core.d.ts#L431

@imcotton
Copy link

that is fundamentally NOT what javascript is

There are on going proposals:

@shaedrich
Copy link

I guess, AI/LLM is out of scope here to make decisions based on them, as they are not a native part of the JavaScript ecosystem and shouldn't become one.

No, the case were specify selected because it is part of ECMAScript spec1.

From es2015.core.d.ts2:

interface String {
    repeat(count: number): string;
}

Footnotes

1. https://tc39.es/ecma262/multipage/text-processing.html#sec-string.prototype.repeat [↩](#user-content-fnref-1-be409749d9b1f931b979ba7be305d5db)

2. https://github.com/microsoft/TypeScript/blob/v5.5.3/src/lib/es2015.core.d.ts#L431 [↩](#user-content-fnref-2-be409749d9b1f931b979ba7be305d5db)

Can you elaborate on how what you are saying contradicts my statement?

@imcotton
Copy link

imcotton commented Jul 21, 2024

as they are not a native part of the JavaScript

I see, my apologies for the confusion. I saw "they are not" as thinking you meant "they" as String.prototype.repeat instead of AI/LLM.

The reason for pulling AI/LLM here is to avoid relying totally on TypeScript's schedule or priorities, recognizing that AI/LLM has its own focused aiming to assist users.

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