Skip to content

Constructor functions as classes #32944

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

Merged
merged 16 commits into from
Aug 19, 2019
Merged

Conversation

sandersn
Copy link
Member

@sandersn sandersn commented Aug 16, 2019

In JS, constructor functions are now classes. That means:

  1. They use the normal class type machinery for creation and caching.
  2. this types work.
  3. Generics work.

And it only adds about 40 lines of code!

Fixes #26833
Fixes #23007

The original test passes but I haven't run any other tests yet, so I
assume the world is now broken.
Instead of overwriting them
1. Mark @class-tagged functions with Class too.
2. Only gather local type parameters of constructor functions.
3. Remove getJSClassType calls with getDeclaredTypeOfSymbol.
4. Add a couple more failing tests.

getDeclaredTypeOfClassOrInterface now needs to understand prototype
assignment. That's next, I think.
1. Binder marks prototype assignments as Class now.
2. Checker merges prototype assignments using the same merge code as for
functions and their declarations. No more intersections.

Many fewer failing tests now.
Even if there are no this-property assignments in them. (Then why are
you using a class?).
It's probably not needed because now it's just a conditional call to
getDeclaredTypeOfSymbol, and I think most callers already know whether
they have a JS constructor function beforehand.
Because all the properties are merged during getDeclaredTypeOfSymbol.
Copy link
Member Author

@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.

Some explanations follow

// note:we overwrite links because we just cloned the symbol
links = symbol as TransientSymbol;
Copy link
Member Author

Choose a reason for hiding this comment

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

the code that's now in mergeJSSymbols is used both here, in getTypeOfFuncClassEnumModule, and in getDeclaredTypeOfClassOrInterface.

if (node && isBinaryExpression(node)) {
// prototype assignments get the outer type parameters of their constructor function
const assignmentKind = getAssignmentDeclarationKind(node);
if (assignmentKind === AssignmentDeclarationKind.Prototype || assignmentKind === AssignmentDeclarationKind.PrototypeProperty) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Object.defineProperty has a number of other limitations and bugs that make it not worthwhile to support yet.

@@ -6157,6 +6152,16 @@ namespace ts {
function getOuterTypeParameters(node: Node, includeThisTypes?: boolean): TypeParameter[] | undefined {
while (true) {
node = node.parent; // TODO: GH#18217 Use SourceFile kind check instead
if (node && isBinaryExpression(node)) {
Copy link
Member Author

Choose a reason for hiding this comment

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

prototype assignments jump from the assignment location to the location of the constructor function and continues walking up the tree looking for type parameters. It does not fork and continue looking from the site of the assignment.

In a hilarious tweet, I provided some code where this difference could be observed, but it was clearly insane. I think this is a good solution, because it makes prototype assignments act as if they are effectively nested inside the constructor function, in the same way that methods are nested inside classes in modern JS.

@@ -6244,7 +6250,7 @@ namespace ts {
const constraint = getBaseConstraintOfType(type);
return !!constraint && isValidBaseType(constraint) && isMixinConstructorType(constraint);
}
return isJSConstructorType(type);
Copy link
Member Author

Choose a reason for hiding this comment

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

The first case of this function now covers constructor functions.

@@ -7504,12 +7512,18 @@ namespace ts {
// will never be observed because a qualified name can't reference signatures.
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) {
type.callSignatures = getSignaturesOfSymbol(symbol);
type.constructSignatures = filter(type.callSignatures, sig => isJSConstructor(sig.declaration));
Copy link
Member Author

Choose a reason for hiding this comment

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

Construct signatures are now constructed just below here, for symbols marked Class | Function only, instead of running this on all functions.

@@ -8744,7 +8758,6 @@ namespace ts {
let type = signature.target ? instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper!) :
signature.unionSignatures ? getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype) :
getReturnTypeFromAnnotation(signature.declaration!) ||
isJSConstructor(signature.declaration) && getJSClassType(getSymbolOfNode(signature.declaration!)) ||
Copy link
Member Author

Choose a reason for hiding this comment

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

this special case also moves into the Class|Function case of resolveAnonymousTypeMembers, where the correct answer is now just classType.

Note that call signatures of constructor functions now correctly fall through to getReturnTypeFromBody, where before they also claimed to return the class type from getJSClassType.

return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent as ClassLikeDeclaration | InterfaceDeclaration)).thisType!;
}
}

// inside x.prototype = { ... }
Copy link
Member Author

Choose a reason for hiding this comment

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

getThisType and checkThisExpression should use the same code to look up the class' thisType, but do not. In particular, checkThisExpression's code, while more complete, is less efficient than this code since it uses checkExpressionCached instead of just grabbing symbol.parent. Since it's a separate change, I'll fix it in a followup PR.

const targetReturnType = isResolvingReturnTypeOfSignature(target) ? anyType : (target.declaration && isJSConstructor(target.declaration)) ?
getJSClassType(target.declaration.symbol)! : getReturnTypeOfSignature(target);
const targetReturnType = isResolvingReturnTypeOfSignature(target) ? anyType
: target.declaration && isJSConstructor(target.declaration) ? getDeclaredTypeOfClassOrInterface(target.declaration.symbol)
Copy link
Member Author

Choose a reason for hiding this comment

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

this allows a constructor function to be assignable BOTH to new (x: number) => C and (x: number) => void. I left it in because it seems ... useful for JS, even if it's not particularly safe.

@@ -22826,39 +22852,35 @@ namespace ts {

// If the symbol of the node has members, treat it like a constructor.
const symbol = getSymbolOfNode(func);
return !!symbol && (symbol.members !== undefined || symbol.exports !== undefined && symbol.exports.get("prototype" as __String) !== undefined);
Copy link
Member Author

Choose a reason for hiding this comment

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

this diff sucks! isJSConstructor is now simpler -- it no longer has to check symbol.exports.get("prototype") since the symbols are merged. And isJSConstructorType is deleted.

@sandersn
Copy link
Member Author

@andrewbranch @orta @PranavSenthilnathan I thought you might be interested in this too.

@sandersn
Copy link
Member Author

@typescript-bot perf test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Aug 16, 2019

Heya @sandersn, I've started to run the perf test suite on this PR at b2e72cf. You can monitor the build here. It should now contribute to this PR's status checks.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

@sandersn
The results of the perf run you requested are in!

Here they are:

Comparison Report - master..32944

Metric master 32944 Delta Best Worst
Angular - node (v12.1.0, x64)
Memory used 325,834k (± 0.01%) 325,914k (± 0.02%) +80k (+ 0.02%) 325,744k 326,118k
Parse Time 1.49s (± 0.65%) 1.48s (± 0.65%) -0.01s (- 0.54%) 1.47s 1.51s
Bind Time 0.76s (± 0.85%) 0.76s (± 0.77%) -0.00s (- 0.13%) 0.74s 0.77s
Check Time 4.24s (± 0.48%) 4.21s (± 0.41%) -0.03s (- 0.68%) 4.18s 4.26s
Emit Time 5.28s (± 0.57%) 5.27s (± 0.74%) -0.01s (- 0.21%) 5.21s 5.38s
Total Time 11.77s (± 0.37%) 11.72s (± 0.42%) -0.05s (- 0.42%) 11.63s 11.84s
Monaco - node (v12.1.0, x64)
Memory used 346,341k (± 0.02%) 345,859k (± 0.02%) -482k (- 0.14%) 345,716k 346,044k
Parse Time 1.23s (± 0.61%) 1.23s (± 0.85%) -0.00s (- 0.41%) 1.20s 1.25s
Bind Time 0.67s (± 0.96%) 0.68s (± 0.73%) +0.00s (+ 0.60%) 0.67s 0.69s
Check Time 4.25s (± 0.40%) 4.27s (± 0.58%) +0.01s (+ 0.33%) 4.21s 4.33s
Emit Time 2.87s (± 0.45%) 2.86s (± 0.71%) -0.01s (- 0.31%) 2.81s 2.91s
Total Time 9.02s (± 0.30%) 9.02s (± 0.54%) +0.00s (+ 0.00%) 8.93s 9.15s
TFS - node (v12.1.0, x64)
Memory used 301,647k (± 0.02%) 301,352k (± 0.01%) -296k (- 0.10%) 301,268k 301,429k
Parse Time 0.95s (± 0.76%) 0.95s (± 1.17%) +0.00s (+ 0.21%) 0.93s 0.98s
Bind Time 0.62s (± 0.93%) 0.63s (± 1.66%) +0.01s (+ 2.10%) 0.62s 0.66s
Check Time 3.85s (± 0.57%) 3.86s (± 0.78%) +0.02s (+ 0.44%) 3.79s 3.94s
Emit Time 2.96s (± 0.65%) 2.98s (± 0.95%) +0.01s (+ 0.40%) 2.91s 3.05s
Total Time 8.38s (± 0.35%) 8.42s (± 0.58%) +0.04s (+ 0.45%) 8.31s 8.55s
Angular - node (v8.9.0, x64)
Memory used 344,542k (± 0.01%) 344,568k (± 0.01%) +26k (+ 0.01%) 344,491k 344,673k
Parse Time 1.99s (± 0.50%) 1.99s (± 0.46%) +0.00s (+ 0.05%) 1.97s 2.01s
Bind Time 0.82s (± 0.73%) 0.82s (± 0.36%) +0.01s (+ 0.86%) 0.82s 0.83s
Check Time 5.04s (± 0.53%) 5.04s (± 0.67%) -0.01s (- 0.12%) 4.98s 5.11s
Emit Time 6.07s (± 0.84%) 6.11s (± 0.91%) +0.04s (+ 0.61%) 5.97s 6.26s
Total Time 13.92s (± 0.52%) 13.96s (± 0.43%) +0.04s (+ 0.28%) 13.85s 14.14s
Monaco - node (v8.9.0, x64)
Memory used 364,112k (± 0.02%) 363,710k (± 0.02%) -402k (- 0.11%) 363,556k 363,788k
Parse Time 1.56s (± 0.43%) 1.56s (± 0.30%) 0.00s ( 0.00%) 1.55s 1.57s
Bind Time 0.88s (± 0.45%) 0.89s (± 0.85%) +0.01s (+ 0.80%) 0.87s 0.90s
Check Time 5.15s (± 1.73%) 5.22s (± 1.70%) +0.07s (+ 1.28%) 5.05s 5.38s
Emit Time 3.15s (± 4.76%) 3.10s (± 4.56%) -0.05s (- 1.43%) 2.93s 3.40s
Total Time 10.74s (± 0.67%) 10.77s (± 0.65%) +0.03s (+ 0.27%) 10.65s 10.96s
TFS - node (v8.9.0, x64)
Memory used 317,849k (± 0.01%) 317,609k (± 0.01%) -240k (- 0.08%) 317,519k 317,696k
Parse Time 1.26s (± 0.71%) 1.26s (± 0.69%) -0.00s (- 0.16%) 1.25s 1.28s
Bind Time 0.69s (± 4.60%) 0.66s (± 0.72%) -0.02s (- 3.35%) 0.65s 0.67s
Check Time 4.47s (± 1.02%) 4.47s (± 0.66%) +0.00s (+ 0.07%) 4.42s 4.53s
Emit Time 3.07s (± 0.45%) 3.08s (± 0.43%) +0.00s (+ 0.13%) 3.04s 3.11s
Total Time 9.50s (± 0.35%) 9.48s (± 0.34%) -0.02s (- 0.19%) 9.43s 9.57s
Angular - node (v8.9.0, x86)
Memory used 195,176k (± 0.02%) 195,154k (± 0.01%) -23k (- 0.01%) 195,083k 195,231k
Parse Time 1.93s (± 0.62%) 1.92s (± 0.59%) -0.01s (- 0.62%) 1.89s 1.94s
Bind Time 0.94s (± 0.81%) 0.94s (± 0.52%) +0.01s (+ 0.85%) 0.93s 0.95s
Check Time 4.61s (± 0.48%) 4.58s (± 0.46%) -0.03s (- 0.67%) 4.54s 4.64s
Emit Time 5.88s (± 0.36%) 5.80s (± 1.42%) -0.08s (- 1.33%) 5.61s 6.03s
Total Time 13.37s (± 0.34%) 13.25s (± 0.62%) -0.12s (- 0.86%) 13.03s 13.43s
Monaco - node (v8.9.0, x86)
Memory used 203,375k (± 0.01%) 203,165k (± 0.01%) -210k (- 0.10%) 203,119k 203,234k
Parse Time 1.62s (± 0.66%) 1.61s (± 0.66%) -0.00s (- 0.12%) 1.59s 1.63s
Bind Time 0.73s (± 1.16%) 0.72s (± 0.46%) -0.01s (- 1.23%) 0.71s 0.73s
Check Time 4.91s (± 0.47%) 4.88s (± 0.53%) -0.03s (- 0.63%) 4.82s 4.93s
Emit Time 3.24s (± 1.28%) 3.18s (± 0.60%) -0.06s (- 1.91%) 3.14s 3.23s
Total Time 10.49s (± 0.59%) 10.39s (± 0.42%) -0.11s (- 1.01%) 10.28s 10.48s
TFS - node (v8.9.0, x86)
Memory used 178,621k (± 0.02%) 178,458k (± 0.02%) -164k (- 0.09%) 178,362k 178,547k
Parse Time 1.32s (± 0.51%) 1.31s (± 0.72%) -0.00s (- 0.30%) 1.29s 1.34s
Bind Time 0.65s (± 0.77%) 0.64s (± 1.04%) -0.00s (- 0.31%) 0.63s 0.66s
Check Time 4.31s (± 0.64%) 4.31s (± 0.57%) +0.00s (+ 0.12%) 4.27s 4.39s
Emit Time 2.87s (± 1.18%) 2.85s (± 0.98%) -0.02s (- 0.66%) 2.79s 2.92s
Total Time 9.14s (± 0.56%) 9.12s (± 0.43%) -0.02s (- 0.20%) 9.02s 9.19s
Angular - node (v9.0.0, x64)
Memory used 344,124k (± 0.01%) 344,118k (± 0.02%) -7k (- 0.00%) 344,011k 344,252k
Parse Time 1.72s (± 0.62%) 1.71s (± 0.61%) -0.01s (- 0.41%) 1.69s 1.74s
Bind Time 0.76s (± 0.68%) 0.77s (± 0.99%) +0.01s (+ 0.66%) 0.76s 0.79s
Check Time 4.80s (± 0.68%) 4.78s (± 0.93%) -0.03s (- 0.58%) 4.70s 4.89s
Emit Time 5.67s (± 1.53%) 5.79s (± 1.42%) +0.12s (+ 2.05%) 5.54s 5.95s
Total Time 12.95s (± 0.68%) 13.04s (± 0.78%) +0.09s (+ 0.67%) 12.75s 13.26s
Monaco - node (v9.0.0, x64)
Memory used 363,758k (± 0.01%) 363,354k (± 0.02%) -404k (- 0.11%) 363,209k 363,529k
Parse Time 1.31s (± 0.85%) 1.32s (± 0.47%) +0.01s (+ 0.53%) 1.31s 1.33s
Bind Time 0.83s (± 1.14%) 0.83s (± 1.38%) -0.00s (- 0.24%) 0.81s 0.85s
Check Time 5.01s (± 1.77%) 5.00s (± 2.02%) -0.01s (- 0.16%) 4.87s 5.20s
Emit Time 3.18s (± 5.35%) 3.16s (± 5.47%) -0.02s (- 0.50%) 2.87s 3.38s
Total Time 10.34s (± 0.92%) 10.32s (± 0.83%) -0.02s (- 0.21%) 10.15s 10.44s
TFS - node (v9.0.0, x64)
Memory used 317,628k (± 0.02%) 317,369k (± 0.02%) -259k (- 0.08%) 317,205k 317,463k
Parse Time 1.04s (± 0.73%) 1.04s (± 0.59%) -0.01s (- 0.57%) 1.03s 1.05s
Bind Time 0.62s (± 0.76%) 0.62s (± 0.84%) +0.00s (+ 0.16%) 0.61s 0.63s
Check Time 4.38s (± 0.71%) 4.38s (± 0.44%) +0.00s (+ 0.02%) 4.34s 4.42s
Emit Time 3.19s (± 0.80%) 3.19s (± 0.62%) +0.00s (+ 0.03%) 3.13s 3.23s
Total Time 9.23s (± 0.62%) 9.23s (± 0.34%) -0.00s (- 0.03%) 9.17s 9.29s
Angular - node (v9.0.0, x86)
Memory used 195,225k (± 0.03%) 195,247k (± 0.01%) +23k (+ 0.01%) 195,209k 195,286k
Parse Time 1.63s (± 0.76%) 1.64s (± 0.51%) +0.01s (+ 0.55%) 1.62s 1.66s
Bind Time 0.88s (± 0.85%) 0.89s (± 0.67%) +0.00s (+ 0.34%) 0.87s 0.90s
Check Time 4.26s (± 0.65%) 4.24s (± 0.33%) -0.02s (- 0.45%) 4.22s 4.28s
Emit Time 5.55s (± 0.81%) 5.53s (± 1.05%) -0.02s (- 0.31%) 5.43s 5.72s
Total Time 12.32s (± 0.55%) 12.30s (± 0.49%) -0.02s (- 0.17%) 12.20s 12.50s
Monaco - node (v9.0.0, x86)
Memory used 203,426k (± 0.02%) 203,237k (± 0.02%) -189k (- 0.09%) 203,148k 203,323k
Parse Time 1.35s (± 0.79%) 1.35s (± 0.83%) 0.00s ( 0.00%) 1.33s 1.38s
Bind Time 0.64s (± 0.63%) 0.64s (± 0.74%) +0.00s (+ 0.63%) 0.63s 0.65s
Check Time 4.69s (± 0.63%) 4.69s (± 0.48%) -0.00s (- 0.02%) 4.63s 4.73s
Emit Time 3.10s (± 0.75%) 3.09s (± 0.61%) -0.01s (- 0.42%) 3.05s 3.15s
Total Time 9.78s (± 0.33%) 9.77s (± 0.40%) -0.01s (- 0.08%) 9.69s 9.84s
TFS - node (v9.0.0, x86)
Memory used 178,649k (± 0.03%) 178,515k (± 0.03%) -134k (- 0.08%) 178,384k 178,639k
Parse Time 1.06s (± 0.77%) 1.06s (± 1.01%) +0.00s (+ 0.09%) 1.04s 1.08s
Bind Time 0.57s (± 0.58%) 0.58s (± 0.63%) +0.01s (+ 1.05%) 0.57s 0.58s
Check Time 4.15s (± 0.57%) 4.14s (± 0.99%) -0.00s (- 0.10%) 4.06s 4.25s
Emit Time 2.81s (± 0.70%) 2.79s (± 0.94%) -0.02s (- 0.71%) 2.70s 2.83s
Total Time 8.58s (± 0.36%) 8.57s (± 0.50%) -0.01s (- 0.08%) 8.51s 8.68s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-142-generic
Architecturex64
Available Memory16 GB
Available Memory1 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v12.1.0, x64)
  • node (v8.9.0, x64)
  • node (v8.9.0, x86)
  • node (v9.0.0, x64)
  • node (v9.0.0, x86)
Scenarios
  • Angular - node (v12.1.0, x64)
  • Angular - node (v8.9.0, x64)
  • Angular - node (v8.9.0, x86)
  • Angular - node (v9.0.0, x64)
  • Angular - node (v9.0.0, x86)
  • Monaco - node (v12.1.0, x64)
  • Monaco - node (v8.9.0, x64)
  • Monaco - node (v8.9.0, x86)
  • Monaco - node (v9.0.0, x64)
  • Monaco - node (v9.0.0, x86)
  • TFS - node (v12.1.0, x64)
  • TFS - node (v8.9.0, x64)
  • TFS - node (v8.9.0, x86)
  • TFS - node (v9.0.0, x64)
  • TFS - node (v9.0.0, x86)
Benchmark Name Iterations
Current 32944 10
Baseline master 10

@@ -2558,11 +2571,18 @@ namespace ts {
node.left.parent = node;
node.right.parent = node;
const lhs = node.left as PropertyAccessEntityNameExpression;
bindPropertyAssignment(lhs.expression, lhs, /*isPrototypeProperty*/ false);
const constructorSymbol = lookupSymbolForPropertyAccess(lhs.expression);
if (constructorSymbol) {
Copy link
Member

Choose a reason for hiding this comment

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

I think this binding should be unconditional, so a merge cross-file can occur. Eg,

// file1.js
function C() {
  this.a = 2;
}
// file2.js
C.prototype.foo = function() { return this.a; };

Copy link
Member Author

Choose a reason for hiding this comment

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

This is pretty complicated because of things like

Outer.Inner = function() { this.a = 1 }
Outer.Inner.prototype = { ... }

Probably needs an iterated declareSymbol just like declarePossiblyMissingNamespace.

Copy link
Member

Choose a reason for hiding this comment

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

Yep, that sounds about right.

}

function bindObjectDefinePrototypeProperty(node: BindableObjectDefinePropertyCall) {
const namespaceSymbol = lookupSymbolForPropertyAccess((node.arguments[0] as PropertyAccessExpression).expression as EntityNameExpression);
if (namespaceSymbol) {
addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class);
Copy link
Member

Choose a reason for hiding this comment

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

same here

Copy link
Member Author

Choose a reason for hiding this comment

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

As I originally suspected, bindPotentiallyMissingNamespaces overlaps a lot with lookupSymbolForPropertyAccess+addDeclarationToSymbol, so the right thing is to put it there. Couple of caveats:

  1. bindObjectDefinePrototypeProperty doesn't call bindPropertyAssignment, so cross-file merges don't work for it currently anyway. I want to merge this PR soon, so I filed JS: Object.defineProperty "undefined namespaces" don't merge cross-file #32979 to track adding that (and testing it, which is the bigger task.)
  2. The symbol binding is still technically conditional, but works in all the cases that cross-file binding does today.
  3. I'm still working on making it elegant, which is why I didn't do it in the first place. Currently I'm passing two booleans, isPrototypeProperty and isClass, which Seems Bad. I'll push a commit after I figure out whether an enum can work well here.

@@ -227,7 +227,8 @@ namespace ts {
symbol.flags |= symbolFlags;

node.symbol = symbol;
symbol.declarations = append(symbol.declarations, node);
// TODO: This is probably too slow to run on every call
Copy link
Member Author

Choose a reason for hiding this comment

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

perf numbers seem fine, so I should remove this TODO

let inferred: Type | undefined;
if (isJSConstructor(symbol.valueDeclaration)) {
inferred = getInferredClassType(symbol);
function mergeJSSymbols(target: Symbol, source: Symbol | undefined) {
Copy link
Member

Choose a reason for hiding this comment

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

If mergeJSSymbols is called twice with the same pair of symbols, you'll get a new output symbol - I think the result of the merge needs to be stored, eg, on the symbol links of the target.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's not future-proof, but we both looked at the callers of mergeJSSymbols and they both cache immediately after calling it.

@sandersn sandersn merged commit 6ca9d04 into master Aug 19, 2019
@sandersn sandersn deleted the constructor-functions-as-classes branch August 19, 2019 21:13
timsuchanek pushed a commit to timsuchanek/TypeScript that referenced this pull request Sep 11, 2019
* Initial implementation

The original test passes but I haven't run any other tests yet, so I
assume the world is now broken.

* Append constructor function construct sigs

Instead of overwriting them

* Grab bag of improvements.

1. Mark @class-tagged functions with Class too.
2. Only gather local type parameters of constructor functions.
3. Remove getJSClassType calls with getDeclaredTypeOfSymbol.
4. Add a couple more failing tests.

getDeclaredTypeOfClassOrInterface now needs to understand prototype
assignment. That's next, I think.

* Prototype assignments work now

1. Binder marks prototype assignments as Class now.
2. Checker merges prototype assignments using the same merge code as for
functions and their declarations. No more intersections.

Many fewer failing tests now.

* Mark prototype-property assignments as Class

Even if there are no this-property assignments in them. (Then why are
you using a class?).

* Simplify getJSClassType, remove calls to its guts

It's probably not needed because now it's just a conditional call to
getDeclaredTypeOfSymbol, and I think most callers already know whether
they have a JS constructor function beforehand.

* isJSDocConstructor doesn't need to check prototype anymore

Because all the properties are merged during getDeclaredTypeOfSymbol.

* outer type parameter lookup follow prototype assignment

* this-type and -expression support in ctor funcs

Pretty cool!

* Fix remaining tests

* Fix minor lint

* Delete now-unused code

* Add class flag to nested class declarations

Also remove old TODOs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

getApplicableRefactors on file with recursive types freezes TSServer In JS, function declarations should allow subsequent prototype assignment
3 participants