Open trusktr opened 1 year ago
Thanks for reposting this here.
I'm in total support of this proposal, rather than breaking changes to the language syntax. It's backwards-compatible, familiar, and featureful - by building atop existing work rather than contributing to a proliferation of standards.
I don't feel like you have to overly worry about trying to support both comments and types into this same syntax.
I use both TypeScript and JSDocs at the same time - TypeScript for my types, and JSDocs for my documentation. I see no reason why the same can't be done here - type comments for types and JSDocs for documentation.
If you're providing per-parameter documentation for a particular function, then JSDocs isn't all that heavy - the "text-to-overhead" ratio would be relatively low. Things only become an issue if you don't need to provide per-parameter documentation (maybe it's a private internal function, where you really don't need to provide a ton of documentation for it, or maybe a simple overall description works better than describing each and every parameter).
Also, I really wished what you are proposing was being considered more seriously.
The comment syntax you're proposing seems very nice to use. I would prefer using that over TypeScript with a compile step any day.
I like your suggestion, because:
But it's verbose to write.
Suppose the strip-markers stuff still all works,
function foo(a /*:number*/, b /*:string*/) /*:boolean*/ {...}
but with alternatives like above and
function foo( a, //:number b, //:string ) { //:boolean ... }
or a combo, depending on taste:
function foo( a, //:number b, //:string ) /* :boolean */ { ... }
Or
function foo( a, //:number - with description b, //:string - with description ) { //:boolean - with description ... }
Similar to one of my above examples, but with an explicit return type+doc:
//: a:number - description for a //: b:number - description for b //: c:number - description for c //: :string - description of return value function method(a, b, c) { return "Val:"+a + b + c; }
Maybe we don't need some of the colons:
// a: number - description for a // continued on multiple lines with a code sample: // ```js // method(a,b,c) // ``` // b: number - description for b // c: number - description for c // :string - description of return value function method(a, b, c) { return "Val:"+a + b + c; }
Now, I'm trying to translate that example from regular comments to :
-style comments:
function foo(a: number, b: string): boolean { /* ... */ }
function foo(
a: number,
b: string
): boolean {
//...
}
function foo(
a: number "with description",
b: string "with description"
): boolean "with description" {
//...
}
function method(
a: number "description for a" detail(
continued on multiple lines with a code sample:
```js
method(a,b,c)
```),
b: number "description for b",
c: number "description for c"
): string "description of return value" {
return "Val: " + a + b + c;
}
Only the syntax is shorter, but we are free to mix it with regular comments:
function method(
a: number "description for a",
// continued on multiple lines with a code sample:
// ```js
// method(a,b,c)
// ```
b: number "description for b",
c: number "description for c"
): string "description of return value" {
return "Val: " + a + b + c;
}
I mean, with the right rules for :
-style comments, we would have improved JS comments.
It seems that way that the descriptions make the code harder to read, more noisy. Also, descriptions can be multi-line, and contain markdown, etc. (VS Code formats multi-line arkdown when displayed in tooltips, etc).
Let's see: taking this example:
function foo(
a, //:number - with description
) { //:boolean - with description
...
}
it might be like this with long description (the start position of the //:boolean comment applies to the last position that can be typed, which happens to be the return value),
function foo(
a, //:number - with a long description that
// spans multiple lines
) { //:boolean - with a long description that
// spans multiple lines and even has a markdown example:
// ```js
// if (foo(123)) do()
// ```
...
}
or perhaps like this (note that in the comment that is above the function, the lack of a named item implies that it documents the return value):
//:boolean - with a long description that
// spans multiple lines and even has a markdown example:
// ```js
// if (foo(123)) do()
// ```
function foo(
//:number - with a long description that
// spans multiple lines
a,
) {
...
}
How would multi-line descriptions work with :
type comments?
Whichever syntax you pick, if you inline them, you're just propagating the same... how should I call it... non-separation of concerns?
I mean this:
const foo = ( a: number ): number => a;
vs
//: foo = number => number;
const foo = a => a;
Why complicate the code for eyes scan? Does that improve the compiler scanning in some way? Does it have any drawback aside from "it feels weird because I'm trained to look at it the other way"?
I ask this mostly because since I've learnt a bit of Haskell, I've been using TS types like this:
type Foo = (a: number) => number;
const foo: Foo = a => a;
and it has been quite simpler to not bother with keywords like interface
meant to look familiar to people coming in from Java/C# and similar places. I think there might be a benefit to this kind of simplicity and separation of the type info from the implementation, as a sort of "header file"... though, that sounds odd
I generally agree (and really wished Typescript let you put a function's type in the preceding line). But there are cases where inline types can be nice, such as with callbacks.
@theScottyJam How would you declare a callback's type? Wouldn't it be part of the receiving function's declaration? How often do you need to specify a particular lambda's type vs the compiler inferring it?
In TS that would be something like
type Receiver = ( callback: ( a : number ) => number ) => (( a : number) => number);
const receiver: Receiver => b => b;
const result = receiver( c => c );
or
type Callback = ( a : number ) => number;
type Receiver = ( callback: Callback ) => Callback;
const receiver: Receiver => b => b;
const result = receiver( c => c );
so in the simplified haskelian comment/type declaration, you'd have:
//: callback = number => number;
//: receiver = callback => callback;
const receiver = $ => $;
const result1 = receiver( $ => $+$ );
const result2 = receiver( receiver );
So, how often would you need to force a type there vs inferrence? Something like
//: receiver = <T>( T => T );
//: callback = string => string;
//: identity = receiver< callback >;
const identity = $ => $;
// the type of String wold be any=>string, so to narrow it one might do
// a
const r1 = identity( String /*: string=>string */);
// or b, just let it be inferred as the type of callback is part of the identity declaration
const r2 = identity( String );
// or c, have some unambiguous syntax that does it before the invocation
// imagine the colon squiggly meaning it's temporary, only for the following line
//:~ String = callback
const r3 = identity( String );
Of course, I'm always in favor of separating type info from implementation detail as I think that makes for nicer code to parse both visually and probably simpler for compilers to not have too complicated parser if it's meant to only accept for an example line comments that start with only //:
and not inline one with /*: /
as well.
How would /*//:
play out in that case, I wonder...
BTW, @theScottyJam could you elaborate on what's that TS doesn't allow that you wish it did? Have an example?
So, how often would you need to force a type there vs inferrence?
Good point. I just looked through one of my repos, and found the answer to be "very little". The only scenarios I could find in this particular repo were places where my callback was being provided with a value of the unknown
type, and I said in the callback parameters to treat it like the any
type instead. I could also imagine scenarios (like you mentioned) where my callback is being given a value of type any
and I want to change that into something more specific in the parameter type.
But that's about it - it seems I really only set the type of a callback's parameter when I'm going to or from the any
type, which isn't a very common thing for me to do. I'm struggling to think of any other kind of scenario where one might want to specify the parameter types on a callback.
BTW, @theScottyJam could you elaborate on what's that TS doesn't allow that you wish it did? Have an example?
Just the ability to put a function's type on the line before, instead of mixing it into the same line.
// TypeScript today
function fetchUser(db: Database, id: string, { skipCache = false }: { skipCache: boolean }): User {
...
}
// A more ideal syntax
: (Database, string, { skipCache: boolean }) => User
function fetchUser(db, id, { skipCache = false }) {
...
}
// Though, honestly, having it in a comment like you're proposing is just as good for me.
//: (Database, string, { skipCache: boolean }) => User
function fetchUser(db, id, { skipCache = false }) {
...
}
How would multi-line descriptions work with
:
type comments?
We may use Template Literals and enclose them with backtick `
:
function foo(
a: number
: `with a long description that
spans multiple lines`
): boolean
: `with a long description that
spans multiple lines and even has a markdown example:
\`\`\`js
if (foo(123)) do()
\`\`\` ` {
// ...
}
Or we may mix them with regular comments:
function foo(
a: number
/* with a long description that
spans multiple lines */
): boolean
/* with a long description that
spans multiple lines and even has a markdown example:
```js
if (foo(123)) do()
``` */ {
// statements...
}
Although that's possible, but I'm agree that :
-style comments would lost their advantage when writing documentation descriptions.
For example, over in https://github.com/microsoft/TypeScript/issues/48650#issuecomment-1721578187, I've described types-in-comments ideas that both:
For convenience, I've pasted that comment below (and it is actually very TS-specific now thinking about it), but also see that issue for some more thoughts.
My question is, how viable is this approach? Can a standard syntax for use inside-of-existing comments be a thing? Why or why not?