Skip to content

In JSDoc, ? of conditional is frequently parsed as postfix-? #27424

New issue

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

By clicking “No 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? No Sign in to your account

Open
ntninja opened this issue Sep 28, 2018 · 11 comments
Open

In JSDoc, ? of conditional is frequently parsed as postfix-? #27424

ntninja opened this issue Sep 28, 2018 · 11 comments
Labels
Bug A bug in TypeScript
Milestone

Comments

@ntninja
Copy link

ntninja commented Sep 28, 2018

TypeScript Version: 3.2.0-dev.20180927
Search Terms: JSDoc conditional types

Using Conditional Types in JSDoc comments confuses the TypeScript parser since the T extends Y ? A : B syntax looks similar to the Y? (meaning Y|null) syntax from JSDoc.

Code

/**
 * @template {{}} T
 * @param {T} o
 * @returns {T extends Readonly<infer U> ? (keyof U)[] : string[]}
 */
function Object_keys(o) {
	let keys = Object.keys(o);
	// @ts-ignore: Type assertion for stripping `Readonly<…>`
	return keys;
}

Expected behavior:

No error should be reported.
The type of Object_keys should be: <T extends {}>(o: T): T extends Readonly<infer U> ? (keyof U)[] : string[].

Actual behavior:

TypeScript compiler shows an error ("?" expected) and mistakes the existing ? operator for JSDoc <type>|null syntax.

The actual type therefor ends up being: <T extends {}>(o: T): T extends Readonly<infer U> | null ? (keyof U)[] : string[].

Possible fixes:

  1. When encountering a top-level (not within parenthesis) ? token in an extends clause, scan ahead and check whether it is followed by an | or & token (type intersection or union operators). If yes, treat it as |null and keep processing; otherwise, assume it's the start of a conditional type declaration. (Requires at least an LF(2) parser.)
  2. Prohibit the JSDoc ? operator in the top-level of an extends clause, always causing it to be treated as the start of the conditional type declaration.

Playground Link: None, Playground does not seem to support JavaScript with JSDoc instead of TypeScript as input.

@ntninja ntninja changed the title TypeScript compiler gets confused by TypeScript compiler gets confused by ? operator when parsing conditional types Sep 28, 2018
@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Sep 28, 2018
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 3.2 milestone Sep 28, 2018
@sandersn
Copy link
Member

  1. Forbid conditional types in JSDoc. Postfix-? support is more important.

(2) is probably the best option if the implementation isn't too messy.

@noinkling
Copy link

I also just ran into this bug (v3.4.3), was very confused about how the null got in there until I found this issue.

@sandersn
Copy link
Member

After discussion with the JSDoc people, who talked to Closure people, here's option (4):

  1. Forbid postfix-? support. It is rarely used and was only included in Closure as legacy syntax.

@sandersn sandersn changed the title TypeScript compiler gets confused by ? operator when parsing conditional types In JSDoc, ? of conditional is frequently parsed as postfix-? May 20, 2019
@sandersn sandersn modified the milestones: TypeScript 3.5.0, Backlog May 20, 2019
@brendankenny
Copy link
Contributor

👍 for (4) Forbid postfix-? support. It's confusing having both pre and post ? variants anyway, especially because in the postfix position it starts looking a lot like a ts optional property.

@goodmind
Copy link

Found same problem when reprinting AST from jsdoc

@ExE-Boss
Copy link
Contributor

I’ve found that moving the ? on a new line makes TypeScript parse it correctly as a conditional type:

/**
 * @template {{}} T
 * @param {T} o
 * @returns {T extends Readonly<infer U>
	? (keyof U)[] : string[]} */
function Object_keys(o) {
	return /** @type {any} */ (Object.keys(o));
}

@sandersn
Copy link
Member

#39123 does not remove postfix-?, but at least it parses ? as a conditional type if possible. @ExE-Boss how does your example behave on a recent build of typescript (4.0 beta or nightly)?

@ExE-Boss
Copy link
Contributor

It works correctly both with and without the new line.

@sandersn
Copy link
Member

That may be as close as we get to closing this bug then, because tuples now use postfix-? to indicate optional elements: [number,string?]. That means we can't get rid of postfix-? parsing entirely.

@ExE-Boss
Copy link
Contributor

ExE-Boss commented Jul 29, 2020

Well, [number, string?] is valid in TypeScript as well.

And the string? bit becomes (string | undefined)? instead of string | null, so it’s not the null union postfix‑?, but is instead the optional tuple element postfix‑?.

I would however expect that [?string?] would become [(string | null | undefined)?] instead of [string | null] as it currently does in TypeScript 4.0‑beta.


Note that [number, string?] in JSDoc incorrectly produces [number, string | null] in TypeScript <= 3.9.

@sandersn
Copy link
Member

I would like to get rid of jsdoc-style postfix-? semantics entirely, including the parsing. It's an old syntax copied from Closure that Closure included for backward compatibility. Its remnants are going to cause minor errors like [?string?] as long as they're around.

I guess we could still get rid of the jsdoc interpretation of string? as string | null, if we made sure it wasn't too big of a breaking change.

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

No branches or pull requests

8 participants