Skip to content

Commit fcd00a5

Browse files
committed
Simplified JS prototype class inference
1 parent 6bb62d6 commit fcd00a5

6 files changed

Lines changed: 27 additions & 110 deletions

File tree

src/compiler/checker.ts

Lines changed: 25 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ namespace ts {
123123

124124
const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
125125

126-
const anySignature = createSignature(undefined, undefined, emptyArray, undefined, anyType, undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false);
127-
const unknownSignature = createSignature(undefined, undefined, emptyArray, undefined, unknownType, undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false);
126+
const anySignature = createSignature(undefined, undefined, emptyArray, anyType, undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false);
127+
const unknownSignature = createSignature(undefined, undefined, emptyArray, unknownType, undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false);
128128

129129
const globals: SymbolTable = {};
130130

@@ -3374,13 +3374,12 @@ namespace ts {
33743374
resolveObjectTypeMembers(type, source, typeParameters, typeArguments);
33753375
}
33763376

3377-
function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], parameters: Symbol[], kind: SignatureKind,
3377+
function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], parameters: Symbol[],
33783378
resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature {
33793379
const sig = new Signature(checker);
33803380
sig.declaration = declaration;
33813381
sig.typeParameters = typeParameters;
33823382
sig.parameters = parameters;
3383-
sig.kind = kind;
33843383
sig.resolvedReturnType = resolvedReturnType;
33853384
sig.typePredicate = typePredicate;
33863385
sig.minArgumentCount = minArgumentCount;
@@ -3390,13 +3389,13 @@ namespace ts {
33903389
}
33913390

33923391
function cloneSignature(sig: Signature): Signature {
3393-
return createSignature(sig.declaration, sig.typeParameters, sig.parameters, sig.kind, sig.resolvedReturnType, sig.typePredicate,
3392+
return createSignature(sig.declaration, sig.typeParameters, sig.parameters, sig.resolvedReturnType, sig.typePredicate,
33943393
sig.minArgumentCount, sig.hasRestParameter, sig.hasStringLiterals);
33953394
}
33963395

33973396
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
33983397
if (!hasClassBaseType(classType)) {
3399-
return [createSignature(undefined, classType.localTypeParameters, emptyArray, SignatureKind.Construct, classType, undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)];
3398+
return [createSignature(undefined, classType.localTypeParameters, emptyArray, classType, undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)];
34003399
}
34013400
const baseConstructorType = getBaseConstructorTypeOfClass(classType);
34023401
const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct);
@@ -3932,41 +3931,15 @@ namespace ts {
39323931
}
39333932
}
39343933

3935-
let kind: SignatureKind;
3936-
switch (declaration.kind) {
3937-
case SyntaxKind.Constructor:
3938-
case SyntaxKind.ConstructSignature:
3939-
case SyntaxKind.ConstructorType:
3940-
kind = SignatureKind.Construct;
3941-
break;
3942-
default:
3943-
if (declaration.symbol.inferredConstructor) {
3944-
kind = SignatureKind.Construct;
3945-
const members = createSymbolTable(emptyArray);
3946-
// Collect methods declared with className.protoype.methodName = ...
3947-
const proto = declaration.symbol.exports["prototype"];
3948-
if (proto) {
3949-
mergeSymbolTable(members, proto.members);
3950-
}
3951-
// Collect properties defined in the constructor by this.propName = ...
3952-
mergeSymbolTable(members, declaration.symbol.members);
3953-
returnType = createAnonymousType(declaration.symbol, members, emptyArray, emptyArray, undefined, undefined);
3954-
}
3955-
else {
3956-
kind = SignatureKind.Call;
3957-
}
3958-
break;
3959-
}
3960-
3961-
links.resolvedSignature = createSignature(declaration, typeParameters, parameters, kind, returnType, typePredicate,
3934+
links.resolvedSignature = createSignature(declaration, typeParameters, parameters, returnType, typePredicate,
39623935
minArgumentCount, hasRestParameter(declaration), hasStringLiterals);
39633936
}
39643937
return links.resolvedSignature;
39653938
}
39663939

39673940
function getSignaturesOfSymbol(symbol: Symbol): Signature[] {
39683941
if (!symbol) return emptyArray;
3969-
let result: Signature[] = [];
3942+
const result: Signature[] = [];
39703943
for (let i = 0, len = symbol.declarations.length; i < len; i++) {
39713944
const node = symbol.declarations[i];
39723945
switch (node.kind) {
@@ -3993,12 +3966,6 @@ namespace ts {
39933966
}
39943967
}
39953968
result.push(getSignatureFromDeclaration(<SignatureDeclaration>node));
3996-
break;
3997-
3998-
case SyntaxKind.PropertyAccessExpression:
3999-
// Inferred class method
4000-
result = getSignaturesOfType(checkExpressionCached((<BinaryExpression>node.parent).right), SignatureKind.Call);
4001-
break;
40023969
}
40033970
}
40043971
return result;
@@ -4081,7 +4048,7 @@ namespace ts {
40814048
// object type literal or interface (using the new keyword). Each way of declaring a constructor
40824049
// will result in a different declaration kind.
40834050
if (!signature.isolatedSignatureType) {
4084-
const isConstructor = signature.kind === SignatureKind.Construct;
4051+
const isConstructor = signature.declaration.kind === SyntaxKind.Constructor || signature.declaration.kind === SyntaxKind.ConstructSignature;
40854052
const type = <ResolvedType>createObjectType(TypeFlags.Anonymous | TypeFlags.FromSignature);
40864053
type.members = emptySymbols;
40874054
type.properties = emptyArray;
@@ -4784,7 +4751,6 @@ namespace ts {
47844751
}
47854752
const result = createSignature(signature.declaration, freshTypeParameters,
47864753
instantiateList(signature.parameters, mapper, instantiateSymbol),
4787-
signature.kind,
47884754
instantiateType(signature.resolvedReturnType, mapper),
47894755
freshTypePredicate,
47904756
signature.minArgumentCount, signature.hasRestParameter, signature.hasStringLiterals);
@@ -6838,14 +6804,7 @@ namespace ts {
68386804
let container: Node;
68396805
if (symbol.flags & SymbolFlags.Class) {
68406806
// get parent of class declaration
6841-
const classDeclaration = getClassLikeDeclarationOfSymbol(symbol);
6842-
if (classDeclaration) {
6843-
container = classDeclaration.parent;
6844-
}
6845-
else {
6846-
// JS-inferred class; do nothing
6847-
return;
6848-
}
6807+
container = getClassLikeDeclarationOfSymbol(symbol).parent;
68496808
}
68506809
else {
68516810
// nesting structure:
@@ -7189,10 +7148,7 @@ namespace ts {
71897148
const operator = binaryExpression.operatorToken.kind;
71907149
if (operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) {
71917150
// In an assignment expression, the right operand is contextually typed by the type of the left operand.
7192-
// In JS files where a special assignment is taking place, don't contextually type the RHS to avoid
7193-
// incorrectly assuming a circular 'any' (the type of the LHS is determined by the RHS)
7194-
if (node === binaryExpression.right &&
7195-
!(node.parserContextFlags & ParserContextFlags.JavaScriptFile && getSpecialPropertyAssignmentKind(binaryExpression))) {
7151+
if (node === binaryExpression.right) {
71967152
return checkExpression(binaryExpression.left);
71977153
}
71987154
}
@@ -9676,9 +9632,21 @@ namespace ts {
96769632
return voidType;
96779633
}
96789634
if (node.kind === SyntaxKind.NewExpression) {
9679-
if (signature.kind === SignatureKind.Call) {
9680-
// When resolved signature is a call signature (and not a construct signature) the result type is any
9681-
if (compilerOptions.noImplicitAny) {
9635+
const declaration = signature.declaration;
9636+
9637+
if (declaration &&
9638+
declaration.kind !== SyntaxKind.Constructor &&
9639+
declaration.kind !== SyntaxKind.ConstructSignature &&
9640+
declaration.kind !== SyntaxKind.ConstructorType) {
9641+
9642+
// When resolved signature is a call signature (and not a construct signature) the result type is any, unless
9643+
// the declaring function had members created through 'x.prototype.y = expr' or 'this.y = expr' psuedodeclarations
9644+
// in a JS file
9645+
const funcSymbol = checkExpression(node.expression).symbol;
9646+
if (funcSymbol && funcSymbol.members && (funcSymbol.flags & SymbolFlags.Function)) {
9647+
return createAnonymousType(undefined, funcSymbol.members, emptyArray, emptyArray, /*stringIndexType*/ undefined, /*numberIndexType*/ undefined);
9648+
}
9649+
else if (compilerOptions.noImplicitAny) {
96829650
error(node, Diagnostics.new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type);
96839651
}
96849652
return anyType;

src/compiler/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2237,7 +2237,6 @@ namespace ts {
22372237
declaration: SignatureDeclaration; // Originating declaration
22382238
typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic)
22392239
parameters: Symbol[]; // Parameters
2240-
kind: SignatureKind; // Call or Construct
22412240
typePredicate?: TypePredicate; // Type predicate
22422241
/* @internal */
22432242
resolvedReturnType: Type; // Resolved return type

src/services/services.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,6 @@ namespace ts {
741741
declaration: SignatureDeclaration;
742742
typeParameters: TypeParameter[];
743743
parameters: Symbol[];
744-
kind: SignatureKind;
745744
resolvedReturnType: Type;
746745
minArgumentCount: number;
747746
hasRestParameter: boolean;

tests/cases/fourslash/javaScriptPrototype1.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
// Members of the class instance
2323
goTo.marker('1');
2424
edit.insert('.');
25-
verify.memberListContains('foo', undefined, undefined, 'method');
26-
verify.memberListContains('bar', undefined, undefined, 'method');
25+
verify.memberListContains('foo', undefined, undefined, 'property');
26+
verify.memberListContains('bar', undefined, undefined, 'property');
2727
edit.backspace();
2828

2929
// Members of a class method (1)

tests/cases/fourslash/javaScriptPrototype3.ts

Lines changed: 0 additions & 40 deletions
This file was deleted.

tests/cases/fourslash/javaScriptPrototype4.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,6 @@ goTo.marker('1');
1616
edit.insert('.');
1717

1818
// Check members of the function
19-
verify.completionListContains('prototype', undefined, undefined, 'property');
2019
verify.completionListContains('foo', undefined, undefined, 'warning');
2120
verify.completionListContains('bar', undefined, undefined, 'warning');
2221
verify.completionListContains('qua', undefined, undefined, 'warning');
23-
24-
// Check members of function.prototype
25-
edit.insert('prototype.');
26-
verify.completionListContains('foo', undefined, undefined, 'method');
27-
verify.completionListContains('bar', undefined, undefined, 'method');
28-
verify.completionListContains('qua', undefined, undefined, 'warning');
29-
verify.completionListContains('prototype', undefined, undefined, 'warning');
30-

0 commit comments

Comments
 (0)