Skip to content

Jsdoc @constructor - in constructor properly infer this as class instance #25980

New issue

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

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged

Conversation

jameskeane
Copy link
Contributor

c.prototype.method = function() { this } was already supported.

This commit add support to two more kinds relying on the JSDoc
@constructor tag. These are:

  1. /** @constructor */ function Example() { this }
  2. /** @constructor */ var Example = function() { this }

Fixes #25979

`c.prototype.method = function() { this }` was already supported.

This commit add support to two more kinds relying on the JSDoc
`@constructor` tag. These are:
 1. `/** @constructor */ function Example() { this }`
 2. `/** @constructor */ var Example = function() { this }`
C3 and C4 `this` was set as `any`, now it is properly showing as
the class type.
@msftclas
Copy link

msftclas commented Jul 26, 2018

CLA assistant check
All CLA requirements met.

@jameskeane
Copy link
Contributor Author

jameskeane commented Jul 26, 2018

This PR doesn't fix the following case, which I need some guidance on:

/** @constructor */
var Example3 = module.exports = function() {
  this.method();
};
Example3.prototype.method = function() {};

The VariableDeclaration correctly infers the member method, but module.exports and the FunctionExpression do not, I would assume the checker would just merge the = binaryops for ref values but it doesn't seem to be doing that.

I looked everywhere for something that walks up binary ops and combines the symbols but could not find it.

EDIT: looks like a special binding for module.exports @ https://github.com/Microsoft/TypeScript/blob/master/src/compiler/binder.ts#L2087

Copy link
Member

@sandersn sandersn left a comment

Choose a reason for hiding this comment

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

I don't think we should take this change until we've discussed and agreed on a solution in the originating bug.

if (classType) {
return getFlowTypeOfReference(node, classType);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this is the correct fix. The type of this should still be any because without an explicit or contextual type for this, you can still assign to any property on it. Instead, the no-implicit-this error should just be suppressed in javascript. The check at the bottom of checkThisExpression should change to if (!type && noImplicitThis && !isInJavascriptFile(node)) { so that JS-based uses of this in constructor functions won't error.

When calling a JS function explicitly tagged with either `@class` or
`@constructor` the checker should throw a TS2348 not callable error.
@jameskeane
Copy link
Contributor Author

@sandersn I've added a test case for the circular initializer example you wanted.

Also, I've included the change that will throw error TS2348: Value of type 'typeof xx' is not callable. when trying to call a function tagged with @class or @constructor.

There was a baseline change to typeFromPropertyAssignment12 that I did not expect; I'm not really sure how to interpret the change either.

I'm running through the user contributed test cases now and will update soon.

Copy link
Member

@sandersn sandersn left a comment

Choose a reason for hiding this comment

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

  1. Probably should move the code that requires @class-marked functions to be called with new.
  2. Some odd indentation.

Otherwise looks good.

// * /** @constructor */ function [name]() { ... }
// * /** @constructor */ var x = function() { ... }
else if ((container.kind === SyntaxKind.FunctionExpression ||
container.kind === SyntaxKind.FunctionDeclaration) &&
Copy link
Member

Choose a reason for hiding this comment

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

indentation looks weird on this line.

@@ -30,7 +30,7 @@ var pos = new Outer.Pos(1, 'x');
>pos : Pos
>new Outer.Pos(1, 'x') : Pos
>Outer.Pos : typeof Pos
>Outer : { (element: any, config: any): void; Pos(line: any, ch: any): void; }
>Outer : { (element: any, config: any): void; }
Copy link
Member

Choose a reason for hiding this comment

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

You're right, this is a result of the change in resolveAnonymousTypeMembers. And it breaks intelllisense -- you get no suggestions for Outer.. I haven't figured out why though.

type.callSignatures = getSignaturesOfSymbol(symbol);
// If the function is explicitly marked with `@class`, then it must be constructed.
if (isInJavaScriptFile(symbol.valueDeclaration) && getJSDocClassTag(symbol.valueDeclaration)) {
type.constructSignatures = getSignaturesOfSymbol(symbol);
Copy link
Member

Choose a reason for hiding this comment

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

given the way this breaks intellisense, perhaps a better change would be to change resolveCallExpression to not resolve call signatures on functions with @constructor instead of switching them entirely to be construct signatures. Something like (just before the last return of the function):

            if (callSignatures.some(sig => isJavaScriptConstructor(sig.declaration) && !!getJSDocClassTag(sig.declaration!))) {
                error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType));
                return resolveErrorCall(node);
            }

Although there's probably a more elegant way to write it. This undoes the questionable baseline changes that happen with the current code.

This undoes the last commit that sought to change how js functions
tagged with `@class` were inferred. For some reason, currently
unknown, giving those functions construct signatures causes issues
in property assignment/member resolution (as seen in the
`typeFromPropertyAssignment12` test case).

Instead of changing the signature resolution, the error is explicitly
generated in `resolveCallExpression` for those functions.
@sandersn sandersn merged commit dfedb24 into microsoft:master Jul 31, 2018
@sandersn
Copy link
Member

@jameskeane Thanks for the suggestion and implementation!

@jameskeane
Copy link
Contributor Author

@sandersn thanks!

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants