@@ -5104,10 +5104,14 @@ namespace ts {
51045104 }
51055105 }
51065106
5107- // Returns true if the interface given by the symbol is free of "this" references. Specifically, the result is
5108- // true if the interface itself contains no references to "this" in its body, if all base types are interfaces,
5109- // and if none of the base interfaces have a "this" type.
5110- function isIndependentInterface(symbol: Symbol): boolean {
5107+ /**
5108+ * Returns true if the interface given by the symbol is free of "this" references.
5109+ *
5110+ * Specifically, the result is true if the interface itself contains no references
5111+ * to "this" in its body, if all base types are interfaces,
5112+ * and if none of the base interfaces have a "this" type.
5113+ */
5114+ function isThislessInterface(symbol: Symbol): boolean {
51115115 for (const declaration of symbol.declarations) {
51125116 if (declaration.kind === SyntaxKind.InterfaceDeclaration) {
51135117 if (declaration.flags & NodeFlags.ContainsThis) {
@@ -5141,7 +5145,7 @@ namespace ts {
51415145 // property types inferred from initializers and method return types inferred from return statements are very hard
51425146 // to exhaustively analyze). We give interfaces a "this" type if we can't definitely determine that they are free of
51435147 // "this" references.
5144- if (outerTypeParameters || localTypeParameters || kind === ObjectFlags.Class || !isIndependentInterface (symbol)) {
5148+ if (outerTypeParameters || localTypeParameters || kind === ObjectFlags.Class || !isThislessInterface (symbol)) {
51455149 type.objectFlags |= ObjectFlags.Reference;
51465150 type.typeParameters = concatenate(outerTypeParameters, localTypeParameters);
51475151 type.outerTypeParameters = outerTypeParameters;
@@ -5323,22 +5327,12 @@ namespace ts {
53235327 return undefined;
53245328 }
53255329
5326- // A type reference is considered independent if each type argument is considered independent.
5327- function isIndependentTypeReference(node: TypeReferenceNode): boolean {
5328- if (node.typeArguments) {
5329- for (const typeNode of node.typeArguments) {
5330- if (!isIndependentType(typeNode)) {
5331- return false;
5332- }
5333- }
5334- }
5335- return true;
5336- }
5337-
5338- // A type is considered independent if it the any, string, number, boolean, symbol, or void keyword, a string
5339- // literal type, an array with an element type that is considered independent, or a type reference that is
5340- // considered independent.
5341- function isIndependentType(node: TypeNode): boolean {
5330+ /**
5331+ * A type is free of this references if it's the any, string, number, boolean, symbol, or void keyword, a string
5332+ * literal type, an array with an element type that is free of this references, or a type reference that is
5333+ * free of this references.
5334+ */
5335+ function isThislessType(node: TypeNode): boolean {
53425336 switch (node.kind) {
53435337 case SyntaxKind.AnyKeyword:
53445338 case SyntaxKind.StringKeyword:
@@ -5353,54 +5347,58 @@ namespace ts {
53535347 case SyntaxKind.LiteralType:
53545348 return true;
53555349 case SyntaxKind.ArrayType:
5356- return isIndependentType ((<ArrayTypeNode>node).elementType);
5350+ return isThislessType ((<ArrayTypeNode>node).elementType);
53575351 case SyntaxKind.TypeReference:
5358- return isIndependentTypeReference(< TypeReferenceNode> node);
5352+ return !(node as TypeReferenceNode).typeArguments || ( node as TypeReferenceNode).typeArguments.every(isThislessType );
53595353 }
53605354 return false;
53615355 }
53625356
5363- // A variable-like declaration is considered independent (free of this references) if it has a type annotation
5364- // that specifies an independent type, or if it has no type annotation and no initializer (and thus of type any).
5365- function isIndependentVariableLikeDeclaration(node: VariableLikeDeclaration): boolean {
5357+ /** A type parameter is thisless if its contraint is thisless, or if it has no constraint. */
5358+ function isThislessTypeParameter(node: TypeParameterDeclaration) {
5359+ return !node.constraint || isThislessType(node.constraint);
5360+ }
5361+
5362+ /**
5363+ * A variable-like declaration is free of this references if it has a type annotation
5364+ * that is thisless, or if it has no type annotation and no initializer (and is thus of type any).
5365+ */
5366+ function isThislessVariableLikeDeclaration(node: VariableLikeDeclaration): boolean {
53665367 const typeNode = getEffectiveTypeAnnotationNode(node);
5367- return typeNode ? isIndependentType (typeNode) : !node.initializer;
5368+ return typeNode ? isThislessType (typeNode) : !node.initializer;
53685369 }
53695370
5370- // A function-like declaration is considered independent (free of this references) if it has a return type
5371- // annotation that is considered independent and if each parameter is considered independent.
5372- function isIndependentFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
5373- if (node.kind !== SyntaxKind.Constructor) {
5374- const typeNode = getEffectiveReturnTypeNode(node);
5375- if (!typeNode || !isIndependentType(typeNode)) {
5376- return false;
5377- }
5378- }
5379- for (const parameter of node.parameters) {
5380- if (!isIndependentVariableLikeDeclaration(parameter)) {
5381- return false;
5382- }
5383- }
5384- return true;
5371+ /**
5372+ * A function-like declaration is considered free of `this` references if it has a return type
5373+ * annotation that is free of this references and if each parameter is thisless and if
5374+ * each type parameter (if present) is thisless.
5375+ */
5376+ function isThislessFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
5377+ const returnType = getEffectiveReturnTypeNode(node);
5378+ return (node.kind === SyntaxKind.Constructor || (returnType && isThislessType(returnType))) &&
5379+ node.parameters.every(isThislessVariableLikeDeclaration) &&
5380+ (!node.typeParameters || node.typeParameters.every(isThislessTypeParameter));
53855381 }
53865382
5387- // Returns true if the class or interface member given by the symbol is free of "this" references. The
5388- // function may return false for symbols that are actually free of "this" references because it is not
5389- // feasible to perform a complete analysis in all cases. In particular, property members with types
5390- // inferred from their initializers and function members with inferred return types are conservatively
5391- // assumed not to be free of "this" references.
5392- function isIndependentMember(symbol: Symbol): boolean {
5383+ /**
5384+ * Returns true if the class or interface member given by the symbol is free of "this" references. The
5385+ * function may return false for symbols that are actually free of "this" references because it is not
5386+ * feasible to perform a complete analysis in all cases. In particular, property members with types
5387+ * inferred from their initializers and function members with inferred return types are conservatively
5388+ * assumed not to be free of "this" references.
5389+ */
5390+ function isThisless(symbol: Symbol): boolean {
53935391 if (symbol.declarations && symbol.declarations.length === 1) {
53945392 const declaration = symbol.declarations[0];
53955393 if (declaration) {
53965394 switch (declaration.kind) {
53975395 case SyntaxKind.PropertyDeclaration:
53985396 case SyntaxKind.PropertySignature:
5399- return isIndependentVariableLikeDeclaration (<VariableLikeDeclaration>declaration);
5397+ return isThislessVariableLikeDeclaration (<VariableLikeDeclaration>declaration);
54005398 case SyntaxKind.MethodDeclaration:
54015399 case SyntaxKind.MethodSignature:
54025400 case SyntaxKind.Constructor:
5403- return isIndependentFunctionLikeDeclaration (<FunctionLikeDeclaration>declaration);
5401+ return isThislessFunctionLikeDeclaration (<FunctionLikeDeclaration>declaration);
54045402 }
54055403 }
54065404 }
@@ -5412,7 +5410,7 @@ namespace ts {
54125410 function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable {
54135411 const result = createSymbolTable();
54145412 for (const symbol of symbols) {
5415- result.set(symbol.escapedName, mappingThisOnly && isIndependentMember (symbol) ? symbol : instantiateSymbol(symbol, mapper));
5413+ result.set(symbol.escapedName, mappingThisOnly && isThisless (symbol) ? symbol : instantiateSymbol(symbol, mapper));
54165414 }
54175415 return result;
54185416 }
0 commit comments