Skip to content

Commit b470976

Browse files
sandersntimsuchanek
authored andcommitted
Fix prototype property type lookup (microsoft#33034)
* Fix constructor function type reference lookup I knew I missed some code in the constructor-functions-as-classes PR. This simplifies the type reference resolution code as well. * Simplify and document js alias type resolution
1 parent 5296c55 commit b470976

File tree

4 files changed

+134
-53
lines changed

4 files changed

+134
-53
lines changed

src/compiler/checker.ts

Lines changed: 44 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2555,6 +2555,22 @@ namespace ts {
25552555
return initializer || decl;
25562556
}
25572557

2558+
/**
2559+
* Get the real symbol of a declaration with an expando initializer.
2560+
*
2561+
* Normally, declarations have an associated symbol, but when a declaration has an expando
2562+
* initializer, the expando's symbol is the one that has all the members merged into it.
2563+
*/
2564+
function getExpandoSymbol(symbol: Symbol): Symbol | undefined {
2565+
const decl = symbol.valueDeclaration;
2566+
if (!decl || !isInJSFile(decl) || symbol.flags & SymbolFlags.TypeAlias) {
2567+
return undefined;
2568+
}
2569+
const init = isVariableDeclaration(decl) ? getDeclaredExpandoInitializer(decl) : getAssignedExpandoInitializer(decl);
2570+
return init && getSymbolOfNode(init) || undefined;
2571+
}
2572+
2573+
25582574
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression, ignoreErrors?: boolean): Symbol | undefined {
25592575
return resolveExternalModuleNameWorker(location, moduleReferenceExpression, ignoreErrors ? undefined : Diagnostics.Cannot_find_module_0);
25602576
}
@@ -9190,10 +9206,13 @@ namespace ts {
91909206
if (symbol === unknownSymbol) {
91919207
return errorType;
91929208
}
9209+
symbol = getExpandoSymbol(symbol) || symbol;
91939210

9194-
const type = getTypeReferenceTypeWorker(node, symbol, typeArguments);
9195-
if (type) {
9196-
return type;
9211+
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
9212+
return getTypeFromClassOrInterfaceReference(node, symbol, typeArguments);
9213+
}
9214+
if (symbol.flags & SymbolFlags.TypeAlias) {
9215+
return getTypeFromTypeAliasReference(node, symbol, typeArguments);
91979216
}
91989217

91999218
// Get type from reference to named type that cannot be generic (enum or type parameter)
@@ -9204,62 +9223,34 @@ namespace ts {
92049223
errorType;
92059224
}
92069225

9207-
if (!(symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node))) {
9208-
return errorType;
9209-
}
9210-
9211-
const jsdocType = getJSDocTypeReference(node, symbol, typeArguments);
9212-
if (jsdocType) {
9213-
return jsdocType;
9226+
if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) {
9227+
const jsdocType = getTypeFromJSAlias(node, symbol);
9228+
if (jsdocType) {
9229+
return jsdocType;
9230+
}
9231+
else {
9232+
// Resolve the type reference as a Type for the purpose of reporting errors.
9233+
resolveTypeReferenceName(getTypeReferenceName(node), SymbolFlags.Type);
9234+
return getTypeOfSymbol(symbol);
9235+
}
92149236
}
92159237

9216-
// Resolve the type reference as a Type for the purpose of reporting errors.
9217-
resolveTypeReferenceName(getTypeReferenceName(node), SymbolFlags.Type);
9218-
return getTypeOfSymbol(symbol);
9238+
return errorType;
92199239
}
92209240

92219241
/**
9222-
* A jsdoc TypeReference may have resolved to a value (as opposed to a type). If
9223-
* the symbol is a constructor function, return the inferred class type; otherwise,
9224-
* the type of this reference is just the type of the value we resolved to.
9242+
* A JSdoc TypeReference may be to a value imported from commonjs.
9243+
* These should really be aliases, but this special-case code fakes alias resolution
9244+
* by producing a type from a value.
92259245
*/
9226-
function getJSDocTypeReference(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[] | undefined): Type | undefined {
9227-
// In the case of an assignment of a function expression (binary expressions, variable declarations, etc.), we will get the
9228-
// correct instance type for the symbol on the LHS by finding the type for RHS. For example if we want to get the type of the symbol `foo`:
9229-
// var foo = function() {}
9230-
// We will find the static type of the assigned anonymous function.
9231-
const staticType = getTypeOfSymbol(symbol);
9232-
const instanceType =
9233-
staticType.symbol &&
9234-
staticType.symbol !== symbol && // Make sure this is an assignment like expression by checking that symbol -> type -> symbol doesn't roundtrips.
9235-
getTypeReferenceTypeWorker(node, staticType.symbol, typeArguments); // Get the instance type of the RHS symbol.
9236-
if (instanceType) {
9237-
return getSymbolLinks(symbol).resolvedJSDocType = instanceType;
9238-
}
9239-
}
9240-
9241-
function getTypeReferenceTypeWorker(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[] | undefined): Type | undefined {
9242-
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
9243-
if (symbol.valueDeclaration && symbol.valueDeclaration.parent && isBinaryExpression(symbol.valueDeclaration.parent)) {
9244-
const jsdocType = getJSDocTypeReference(node, symbol, typeArguments);
9245-
if (jsdocType) {
9246-
return jsdocType;
9247-
}
9248-
}
9249-
return getTypeFromClassOrInterfaceReference(node, symbol, typeArguments);
9250-
}
9251-
9252-
if (symbol.flags & SymbolFlags.TypeAlias) {
9253-
return getTypeFromTypeAliasReference(node, symbol, typeArguments);
9254-
}
9255-
9256-
if (symbol.flags & SymbolFlags.Function &&
9257-
isJSDocTypeReference(node) &&
9258-
isJSConstructor(symbol.valueDeclaration)) {
9259-
const resolved = resolveStructuredTypeMembers(<ObjectType>getTypeOfSymbol(symbol));
9260-
if (resolved.callSignatures.length === 1) {
9261-
return getReturnTypeOfSignature(resolved.callSignatures[0]);
9262-
}
9246+
function getTypeFromJSAlias(node: NodeWithTypeArguments, symbol: Symbol): Type | undefined {
9247+
const valueType = getTypeOfSymbol(symbol);
9248+
const typeType =
9249+
valueType.symbol &&
9250+
valueType.symbol !== symbol && // Make sure this is a commonjs export by checking that symbol -> type -> symbol doesn't roundtrip.
9251+
getTypeReferenceType(node, valueType.symbol);
9252+
if (typeType) {
9253+
return getSymbolLinks(symbol).resolvedJSDocType = typeType;
92639254
}
92649255
}
92659256

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
=== tests/cases/conformance/salsa/jsdocConstructorFunctionTypeReference.js ===
2+
var Validator = function VFunc() {
3+
>Validator : Symbol(Validator, Decl(jsdocConstructorFunctionTypeReference.js, 0, 3))
4+
>VFunc : Symbol(VFunc, Decl(jsdocConstructorFunctionTypeReference.js, 0, 15))
5+
6+
this.flags = "gim"
7+
>flags : Symbol(VFunc.flags, Decl(jsdocConstructorFunctionTypeReference.js, 0, 34))
8+
9+
};
10+
11+
Validator.prototype.num = 12
12+
>Validator.prototype : Symbol(Validator.num, Decl(jsdocConstructorFunctionTypeReference.js, 2, 2))
13+
>Validator : Symbol(Validator, Decl(jsdocConstructorFunctionTypeReference.js, 0, 3))
14+
>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --))
15+
>num : Symbol(Validator.num, Decl(jsdocConstructorFunctionTypeReference.js, 2, 2))
16+
17+
/**
18+
* @param {Validator} state
19+
*/
20+
var validateRegExpFlags = function(state) {
21+
>validateRegExpFlags : Symbol(validateRegExpFlags, Decl(jsdocConstructorFunctionTypeReference.js, 9, 3))
22+
>state : Symbol(state, Decl(jsdocConstructorFunctionTypeReference.js, 9, 35))
23+
24+
return state.flags
25+
>state.flags : Symbol(VFunc.flags, Decl(jsdocConstructorFunctionTypeReference.js, 0, 34))
26+
>state : Symbol(state, Decl(jsdocConstructorFunctionTypeReference.js, 9, 35))
27+
>flags : Symbol(VFunc.flags, Decl(jsdocConstructorFunctionTypeReference.js, 0, 34))
28+
29+
};
30+
31+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
=== tests/cases/conformance/salsa/jsdocConstructorFunctionTypeReference.js ===
2+
var Validator = function VFunc() {
3+
>Validator : typeof VFunc
4+
>function VFunc() { this.flags = "gim"} : typeof VFunc
5+
>VFunc : typeof VFunc
6+
7+
this.flags = "gim"
8+
>this.flags = "gim" : "gim"
9+
>this.flags : any
10+
>this : any
11+
>flags : any
12+
>"gim" : "gim"
13+
14+
};
15+
16+
Validator.prototype.num = 12
17+
>Validator.prototype.num = 12 : 12
18+
>Validator.prototype.num : any
19+
>Validator.prototype : any
20+
>Validator : typeof VFunc
21+
>prototype : any
22+
>num : any
23+
>12 : 12
24+
25+
/**
26+
* @param {Validator} state
27+
*/
28+
var validateRegExpFlags = function(state) {
29+
>validateRegExpFlags : (state: VFunc) => string
30+
>function(state) { return state.flags} : (state: VFunc) => string
31+
>state : VFunc
32+
33+
return state.flags
34+
>state.flags : string
35+
>state : VFunc
36+
>flags : string
37+
38+
};
39+
40+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// @noEmit: true
2+
// @allowJs: true
3+
// @checkJs: true
4+
// @noImplicitAny: true
5+
// @Filename: jsdocConstructorFunctionTypeReference.js
6+
7+
var Validator = function VFunc() {
8+
this.flags = "gim"
9+
};
10+
11+
Validator.prototype.num = 12
12+
13+
/**
14+
* @param {Validator} state
15+
*/
16+
var validateRegExpFlags = function(state) {
17+
return state.flags
18+
};
19+

0 commit comments

Comments
 (0)