Skip to content

Constructor functions as classes - #32944

Merged
Nathan Shively-Sanders (sandersn) merged 16 commits into
masterfrom
constructor-functions-as-classes
Aug 19, 2019
Merged

Constructor functions as classes#32944
Nathan Shively-Sanders (sandersn) merged 16 commits into
masterfrom
constructor-functions-as-classes

Conversation

@sandersn

@sandersnNathan Shively-Sanders (sandersn) commented Aug 16, 2019

Copy link
Copy Markdown
Member

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.
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
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Some explanations follow

const merged = mergeJSSymbols(symbol, getSymbolOfNode(jsDeclaration));
if (merged) {
// note:we overwrite links because we just cloned the symbol
links = symbol as TransientSymbol;

Copy link
Copy Markdown
MemberAuthor

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
Copy Markdown
MemberAuthor

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.

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
Copy Markdown
MemberAuthor

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.

const constraint = getBaseConstraintOfType(type);
return !!constraint && isValidBaseType(constraint) && isMixinConstructorType(constraint);
}
return isJSConstructorType(type);

Copy link
Copy Markdown
MemberAuthor

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.

// 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
Copy Markdown
MemberAuthor

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.

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
Copy Markdown
MemberAuthor

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.

}
}

// inside x.prototype = { ... }

Copy link
Copy Markdown
MemberAuthor

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
Copy Markdown
MemberAuthor

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.


// 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
Copy Markdown
MemberAuthor

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
Copy Markdown
MemberAuthor

@sandersn

Copy link
Copy Markdown
MemberAuthor

TypeScript Bot (@typescript-bot) perf test this

@typescript-bot

TypeScript Bot (typescript-bot) commented Aug 16, 2019

Copy link
Copy Markdown
Contributor

Heya Nathan Shively-Sanders (@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
Copy Markdown
Contributor

Nathan Shively-Sanders (@sandersn)
The results of the perf run you requested are in!

Here they are:

Comparison Report - master..32944

Metricmaster32944DeltaBestWorst
Angular - node (v12.1.0, x64)
Memory used325,834k (± 0.01%)325,914k (± 0.02%)+80k (+ 0.02%)325,744k326,118k
Parse Time1.49s (± 0.65%)1.48s (± 0.65%)-0.01s (- 0.54%)1.47s1.51s
Bind Time0.76s (± 0.85%)0.76s (± 0.77%)-0.00s (- 0.13%)0.74s0.77s
Check Time4.24s (± 0.48%)4.21s (± 0.41%)-0.03s (- 0.68%)4.18s4.26s
Emit Time5.28s (± 0.57%)5.27s (± 0.74%)-0.01s (- 0.21%)5.21s5.38s
Total Time11.77s (± 0.37%)11.72s (± 0.42%)-0.05s (- 0.42%)11.63s11.84s
Monaco - node (v12.1.0, x64)
Memory used346,341k (± 0.02%)345,859k (± 0.02%)-482k (- 0.14%)345,716k346,044k
Parse Time1.23s (± 0.61%)1.23s (± 0.85%)-0.00s (- 0.41%)1.20s1.25s
Bind Time0.67s (± 0.96%)0.68s (± 0.73%)+0.00s (+ 0.60%)0.67s0.69s
Check Time4.25s (± 0.40%)4.27s (± 0.58%)+0.01s (+ 0.33%)4.21s4.33s
Emit Time2.87s (± 0.45%)2.86s (± 0.71%)-0.01s (- 0.31%)2.81s2.91s
Total Time9.02s (± 0.30%)9.02s (± 0.54%)+0.00s (+ 0.00%)8.93s9.15s
TFS - node (v12.1.0, x64)
Memory used301,647k (± 0.02%)301,352k (± 0.01%)-296k (- 0.10%)301,268k301,429k
Parse Time0.95s (± 0.76%)0.95s (± 1.17%)+0.00s (+ 0.21%)0.93s0.98s
Bind Time0.62s (± 0.93%)0.63s (± 1.66%)+0.01s (+ 2.10%)0.62s0.66s
Check Time3.85s (± 0.57%)3.86s (± 0.78%)+0.02s (+ 0.44%)3.79s3.94s
Emit Time2.96s (± 0.65%)2.98s (± 0.95%)+0.01s (+ 0.40%)2.91s3.05s
Total Time8.38s (± 0.35%)8.42s (± 0.58%)+0.04s (+ 0.45%)8.31s8.55s
Angular - node (v8.9.0, x64)
Memory used344,542k (± 0.01%)344,568k (± 0.01%)+26k (+ 0.01%)344,491k344,673k
Parse Time1.99s (± 0.50%)1.99s (± 0.46%)+0.00s (+ 0.05%)1.97s2.01s
Bind Time0.82s (± 0.73%)0.82s (± 0.36%)+0.01s (+ 0.86%)0.82s0.83s
Check Time5.04s (± 0.53%)5.04s (± 0.67%)-0.01s (- 0.12%)4.98s5.11s
Emit Time6.07s (± 0.84%)6.11s (± 0.91%)+0.04s (+ 0.61%)5.97s6.26s
Total Time13.92s (± 0.52%)13.96s (± 0.43%)+0.04s (+ 0.28%)13.85s14.14s
Monaco - node (v8.9.0, x64)
Memory used364,112k (± 0.02%)363,710k (± 0.02%)-402k (- 0.11%)363,556k363,788k
Parse Time1.56s (± 0.43%)1.56s (± 0.30%)0.00s ( 0.00%)1.55s1.57s
Bind Time0.88s (± 0.45%)0.89s (± 0.85%)+0.01s (+ 0.80%)0.87s0.90s
Check Time5.15s (± 1.73%)5.22s (± 1.70%)+0.07s (+ 1.28%)5.05s5.38s
Emit Time3.15s (± 4.76%)3.10s (± 4.56%)-0.05s (- 1.43%)2.93s3.40s
Total Time10.74s (± 0.67%)10.77s (± 0.65%)+0.03s (+ 0.27%)10.65s10.96s
TFS - node (v8.9.0, x64)
Memory used317,849k (± 0.01%)317,609k (± 0.01%)-240k (- 0.08%)317,519k317,696k
Parse Time1.26s (± 0.71%)1.26s (± 0.69%)-0.00s (- 0.16%)1.25s1.28s
Bind Time0.69s (± 4.60%)0.66s (± 0.72%)-0.02s (- 3.35%)0.65s0.67s
Check Time4.47s (± 1.02%)4.47s (± 0.66%)+0.00s (+ 0.07%)4.42s4.53s
Emit Time3.07s (± 0.45%)3.08s (± 0.43%)+0.00s (+ 0.13%)3.04s3.11s
Total Time9.50s (± 0.35%)9.48s (± 0.34%)-0.02s (- 0.19%)9.43s9.57s
Angular - node (v8.9.0, x86)
Memory used195,176k (± 0.02%)195,154k (± 0.01%)-23k (- 0.01%)195,083k195,231k
Parse Time1.93s (± 0.62%)1.92s (± 0.59%)-0.01s (- 0.62%)1.89s1.94s
Bind Time0.94s (± 0.81%)0.94s (± 0.52%)+0.01s (+ 0.85%)0.93s0.95s
Check Time4.61s (± 0.48%)4.58s (± 0.46%)-0.03s (- 0.67%)4.54s4.64s
Emit Time5.88s (± 0.36%)5.80s (± 1.42%)-0.08s (- 1.33%)5.61s6.03s
Total Time13.37s (± 0.34%)13.25s (± 0.62%)-0.12s (- 0.86%)13.03s13.43s
Monaco - node (v8.9.0, x86)
Memory used203,375k (± 0.01%)203,165k (± 0.01%)-210k (- 0.10%)203,119k203,234k
Parse Time1.62s (± 0.66%)1.61s (± 0.66%)-0.00s (- 0.12%)1.59s1.63s
Bind Time0.73s (± 1.16%)0.72s (± 0.46%)-0.01s (- 1.23%)0.71s0.73s
Check Time4.91s (± 0.47%)4.88s (± 0.53%)-0.03s (- 0.63%)4.82s4.93s
Emit Time3.24s (± 1.28%)3.18s (± 0.60%)-0.06s (- 1.91%)3.14s3.23s
Total Time10.49s (± 0.59%)10.39s (± 0.42%)-0.11s (- 1.01%)10.28s10.48s
TFS - node (v8.9.0, x86)
Memory used178,621k (± 0.02%)178,458k (± 0.02%)-164k (- 0.09%)178,362k178,547k
Parse Time1.32s (± 0.51%)1.31s (± 0.72%)-0.00s (- 0.30%)1.29s1.34s
Bind Time0.65s (± 0.77%)0.64s (± 1.04%)-0.00s (- 0.31%)0.63s0.66s
Check Time4.31s (± 0.64%)4.31s (± 0.57%)+0.00s (+ 0.12%)4.27s4.39s
Emit Time2.87s (± 1.18%)2.85s (± 0.98%)-0.02s (- 0.66%)2.79s2.92s
Total Time9.14s (± 0.56%)9.12s (± 0.43%)-0.02s (- 0.20%)9.02s9.19s
Angular - node (v9.0.0, x64)
Memory used344,124k (± 0.01%)344,118k (± 0.02%)-7k (- 0.00%)344,011k344,252k
Parse Time1.72s (± 0.62%)1.71s (± 0.61%)-0.01s (- 0.41%)1.69s1.74s
Bind Time0.76s (± 0.68%)0.77s (± 0.99%)+0.01s (+ 0.66%)0.76s0.79s
Check Time4.80s (± 0.68%)4.78s (± 0.93%)-0.03s (- 0.58%)4.70s4.89s
Emit Time5.67s (± 1.53%)5.79s (± 1.42%)+0.12s (+ 2.05%)5.54s5.95s
Total Time12.95s (± 0.68%)13.04s (± 0.78%)+0.09s (+ 0.67%)12.75s13.26s
Monaco - node (v9.0.0, x64)
Memory used363,758k (± 0.01%)363,354k (± 0.02%)-404k (- 0.11%)363,209k363,529k
Parse Time1.31s (± 0.85%)1.32s (± 0.47%)+0.01s (+ 0.53%)1.31s1.33s
Bind Time0.83s (± 1.14%)0.83s (± 1.38%)-0.00s (- 0.24%)0.81s0.85s
Check Time5.01s (± 1.77%)5.00s (± 2.02%)-0.01s (- 0.16%)4.87s5.20s
Emit Time3.18s (± 5.35%)3.16s (± 5.47%)-0.02s (- 0.50%)2.87s3.38s
Total Time10.34s (± 0.92%)10.32s (± 0.83%)-0.02s (- 0.21%)10.15s10.44s
TFS - node (v9.0.0, x64)
Memory used317,628k (± 0.02%)317,369k (± 0.02%)-259k (- 0.08%)317,205k317,463k
Parse Time1.04s (± 0.73%)1.04s (± 0.59%)-0.01s (- 0.57%)1.03s1.05s
Bind Time0.62s (± 0.76%)0.62s (± 0.84%)+0.00s (+ 0.16%)0.61s0.63s
Check Time4.38s (± 0.71%)4.38s (± 0.44%)+0.00s (+ 0.02%)4.34s4.42s
Emit Time3.19s (± 0.80%)3.19s (± 0.62%)+0.00s (+ 0.03%)3.13s3.23s
Total Time9.23s (± 0.62%)9.23s (± 0.34%)-0.00s (- 0.03%)9.17s9.29s
Angular - node (v9.0.0, x86)
Memory used195,225k (± 0.03%)195,247k (± 0.01%)+23k (+ 0.01%)195,209k195,286k
Parse Time1.63s (± 0.76%)1.64s (± 0.51%)+0.01s (+ 0.55%)1.62s1.66s
Bind Time0.88s (± 0.85%)0.89s (± 0.67%)+0.00s (+ 0.34%)0.87s0.90s
Check Time4.26s (± 0.65%)4.24s (± 0.33%)-0.02s (- 0.45%)4.22s4.28s
Emit Time5.55s (± 0.81%)5.53s (± 1.05%)-0.02s (- 0.31%)5.43s5.72s
Total Time12.32s (± 0.55%)12.30s (± 0.49%)-0.02s (- 0.17%)12.20s12.50s
Monaco - node (v9.0.0, x86)
Memory used203,426k (± 0.02%)203,237k (± 0.02%)-189k (- 0.09%)203,148k203,323k
Parse Time1.35s (± 0.79%)1.35s (± 0.83%)0.00s ( 0.00%)1.33s1.38s
Bind Time0.64s (± 0.63%)0.64s (± 0.74%)+0.00s (+ 0.63%)0.63s0.65s
Check Time4.69s (± 0.63%)4.69s (± 0.48%)-0.00s (- 0.02%)4.63s4.73s
Emit Time3.10s (± 0.75%)3.09s (± 0.61%)-0.01s (- 0.42%)3.05s3.15s
Total Time9.78s (± 0.33%)9.77s (± 0.40%)-0.01s (- 0.08%)9.69s9.84s
TFS - node (v9.0.0, x86)
Memory used178,649k (± 0.03%)178,515k (± 0.03%)-134k (- 0.08%)178,384k178,639k
Parse Time1.06s (± 0.77%)1.06s (± 1.01%)+0.00s (+ 0.09%)1.04s1.08s
Bind Time0.57s (± 0.58%)0.58s (± 0.63%)+0.01s (+ 1.05%)0.57s0.58s
Check Time4.15s (± 0.57%)4.14s (± 0.99%)-0.00s (- 0.10%)4.06s4.25s
Emit Time2.81s (± 0.70%)2.79s (± 0.94%)-0.02s (- 0.71%)2.70s2.83s
Total Time8.58s (± 0.36%)8.57s (± 0.50%)-0.01s (- 0.08%)8.51s8.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)
BenchmarkNameIterations
Current3294410
Baselinemaster10

Comment threadsrc/compiler/binder.ts Outdated
const lhs = node.left as PropertyAccessEntityNameExpression;
bindPropertyAssignment(lhs.expression, lhs, /*isPrototypeProperty*/ false);
const constructorSymbol = lookupSymbolForPropertyAccess(lhs.expression);
if (constructorSymbol) {

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.

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

// file1.jsfunctionC(){this.a=2;}// file2.jsC.prototype.foo=function(){returnthis.a;};

Copy link
Copy Markdown
MemberAuthor

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
Copy Markdown
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.

Comment threadsrc/compiler/binder.ts Outdated
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
Copy Markdown
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
Copy Markdown
MemberAuthor

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.

Comment threadsrc/compiler/binder.ts Outdated

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

Copy link
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Nathan Shively-Sanders (sandersn) deleted the constructor-functions-as-classes branch August 19, 2019 21:13
Tim Suchanek (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
@microsoftMicrosoft (microsoft) locked as resolved and limited conversation to collaborators Oct 21, 2025
Sign up for freeto 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.

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

3 participants

@sandersn@typescript-bot@weswigham