-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Fix crash in JS declaration emit #38508
Conversation
src/compiler/checker.ts
Outdated
// A = {}; | ||
// A.prototype.b = {}; | ||
const type = getTypeOfSymbol(symbol); | ||
return some(getSignaturesOfType(type, SignatureKind.Construct)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, thinking about it, is there a way to adjust the binder to only apply the Class
flag if the thing is actually constructable?
Alternatively, should we still emit this as a class, but with a private
constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Binder has no access to type information, so there's no way to be sure because of augmentations, merging globals, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the private
constructor suggestion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can look into it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the sake of cataloging our behavior, adding
class B extends A {}
(new B()).b;
to the test might be worthwhile, too. I honestly don't know how we type check it offhand, but checking that the declarations are somewhat sensible (or at least in line with any errors we may produce) would seem to make sense.
Hmm. Seems like there's more to do here: // .js
let A;
A = {};
A.prototype.b = {};
class B extends A {}
const x = (new B()).b;
// .d.ts
declare class A {
private constructor();
b: {};
}
declare class B {
}
declare const x: any; The For the equivalent TS, the output is this: // .ts
class A {
private constructor() { }
b: "x" = "x";
}
// @ts-ignore
class B extends A { }
// @ts-ignore
const x = (new B()).b;
// .d.ts
declare class A {
private constructor();
b: "x";
}
declare class B extends A {
}
declare const x: any; So we're consistent on getting |
Hm, the emitting errors part is probably important (declaration emit in the presence thereof a little less so). I agree, we should probably add the error. It's certainly odd that we allow it as a base class when it's not actually constructable... The js does error at runtime, too, right? |
Yes, the JS would error at runtime. The JS decl emit differs because the original declaration of |
* upstream/master: Use control flow analysis to check 'super(...)' call before 'this' access (microsoft#38612) LEGO: check in for master to temporary branch. Make `processTaggedTemplateExpression` visit a returned node goToDefinition: find only the value if it's the RHS of an assignment Fix regression organize imports duplicates comments (microsoft#38599) Fix crash in JS declaration emit (microsoft#38508)
Our binder has a heuristic that detects
X.prototype.y
assignment in a JS file and treatsX
as a class, even ifX
isn't constructible. That results in a crash in JS declaration emit. This adds a check to ensure that the symbol forX
actually has valid signatures.Fixes #36273