Skip to content

Infer typedef variable declaration to never#36454

Closed
TimvdLippe wants to merge 1 commit into
microsoft:masterfrom
TimvdLippe:typedef-never
Closed

Infer typedef variable declaration to never#36454
TimvdLippe wants to merge 1 commit into
microsoft:masterfrom
TimvdLippe:typedef-never

Conversation

@TimvdLippe

Copy link
Copy Markdown
Contributor

Typedefs have two forms: specify a name in the @typedef declaration or
use the name of the variable declaration it is attached to. Since
@typedef types should only live in the type space, any reference to a
variable that has a @typedef attached to is illegal. As such, assign
it the never type if the @typedef itself does not specify a name.

Fixes #36375

Typedefs have two forms: specify a name in the `@typedef` declaration or
use the name of the variable declaration it is attached to. Since
`@typedef` types should only live in the type space, any reference to a
variable that has a `@typedef` attached to is illegal. As such, assign
it the `never` type if the `@typedef` itself does not specify a name.

Fixes microsoft#36375

@sandersn sandersn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of comments on the code.

Also, I don't know how valuable this is. Its value is based on the number of closure projects that are going to migrate directly to "noImplicitAny": true. Any idea how many that would be? (I assume that they're all inside google.)

Comment thread src/compiler/checker.ts
// The typedef is attached to a variable declaration, instead of specifying the name in the typedef itself
// This means that the variable declared references the typedef and should not be used in regular control
// flow of the program, only in jsdoc type references.
if (jsDocTypeDefTag && !jsDocTypeDefTag.name && isVariableDeclaration(declaration)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably need to check that there is no initialiser too

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, and isInJSFile, no exports, not a binding pattern, not a declare (which is illegal in js, but might show up anyway).

Comment thread src/compiler/checker.ts
return neverType;
}

if ((noImplicitAny || isInJSFile(declaration)) &&

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, maybe the new code should be moved inside here.

@TimvdLippe

Copy link
Copy Markdown
Contributor Author

Yesterday, I realized that noImplicitAny was only pointing out the root problem. TS allowed references to @typedef variable declarations in value space rather than type space. I think that is a bug that warrants fixing (which is this PR). That said, noImplicitAny is one of the main reasons we are migrating to TS. I guess we could roll a fork during our migration (which is expected to take months/years), but I would rather fix it upstream 😄

I will process your code review tomorrow, thanks for taking a look!

@ahejlsberg

Copy link
Copy Markdown
Member

I'm not crazy about the use of type never for the variable (recall, never is assignable to anything). The never type is supposed to represent values that never exist, but clearly the variable does. I think it would be better to use type undefined and verify that the variable has no initializer. That would then reflect the true type of the variable and would ensure nothing other than undefined is assignable to it.

@sandersn

sandersn commented Jan 28, 2020

Copy link
Copy Markdown
Member

@ahejlsberg the intent is to prevent using the type alias variable as an assignment source, so never would be better.

But probably the correct fix is to give an error whenever the variable is referenced in a value position and then suppress the implicit any error. Then the type can remain any.

Edit: Since closure verifies that there is no initialiser, I wouldn't bother making Typescript give an error; just treat it like a normal declaration:

/** @typedef {number} */
var Typename = 12; // value references to Typename should be fine

@ahejlsberg

Copy link
Copy Markdown
Member

the intent is to prevent using the type alias variable as an assignment source, so never would be better

You mean assignment target, yes? If you give it type never it can be used as an assignment source for anything.

What exactly are we trying to prevent? Reads of the variable, writes to the variable, or both?

@sandersn

Copy link
Copy Markdown
Member

Yep, I got it backwards. never is wrong.

We're trying to prevent both reads and writes of the variable since it's only there so Closure can have a name for the associated type alias. I think an error on value-space usage of the variable is better than changing the type, plus suppression of the implicit any error.

@TimvdLippe

Copy link
Copy Markdown
Contributor Author

@sandersn Shall I close this in favor of #28162 ?

@sandersn

sandersn commented Feb 3, 2020

Copy link
Copy Markdown
Member

Yes, I think that's the right path forward.

@TimvdLippe TimvdLippe closed this Feb 3, 2020
@TimvdLippe TimvdLippe deleted the typedef-never branch February 3, 2020 22:07
@microsoft microsoft locked as resolved and limited conversation to collaborators Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nameless @typedef has implicit any error on the associated identifier

3 participants