mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-18 07:29:16 -05:00
Check function type parameters for this references
Previously, when checking for this references, the compiler did not check type parameters. This caused it to miss some instantiations that were necessary. Also some cleanup: 1. Rename isIndependent* to is*FreeOfThisReference. 'Independent' was a one-off concept that was unique to Typescript (and isn't referenced in the spec), so I think the new name is friendlier to new readers. 2. Use `Array.every` whenever possible, since Typescript added a dependency on ES5 since the code was first written. 3. Switch to JSDoc so that it shows up nicely in editors.
This commit is contained in:
@@ -5108,10 +5108,14 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the interface given by the symbol is free of "this" references. Specifically, the result is
|
||||
// true if the interface itself contains no references to "this" in its body, if all base types are interfaces,
|
||||
// and if none of the base interfaces have a "this" type.
|
||||
function isIndependentInterface(symbol: Symbol): boolean {
|
||||
/**
|
||||
* Returns true if the interface given by the symbol is free of "this" references.
|
||||
*
|
||||
* Specifically, the result is true if the interface itself contains no references
|
||||
* to "this" in its body, if all base types are interfaces,
|
||||
* and if none of the base interfaces have a "this" type.
|
||||
*/
|
||||
function isInterfaceFreeOfThisReference(symbol: Symbol): boolean {
|
||||
for (const declaration of symbol.declarations) {
|
||||
if (declaration.kind === SyntaxKind.InterfaceDeclaration) {
|
||||
if (declaration.flags & NodeFlags.ContainsThis) {
|
||||
@@ -5145,7 +5149,7 @@ namespace ts {
|
||||
// property types inferred from initializers and method return types inferred from return statements are very hard
|
||||
// to exhaustively analyze). We give interfaces a "this" type if we can't definitely determine that they are free of
|
||||
// "this" references.
|
||||
if (outerTypeParameters || localTypeParameters || kind === ObjectFlags.Class || !isIndependentInterface(symbol)) {
|
||||
if (outerTypeParameters || localTypeParameters || kind === ObjectFlags.Class || !isInterfaceFreeOfThisReference(symbol)) {
|
||||
type.objectFlags |= ObjectFlags.Reference;
|
||||
type.typeParameters = concatenate(outerTypeParameters, localTypeParameters);
|
||||
type.outerTypeParameters = outerTypeParameters;
|
||||
@@ -5327,22 +5331,17 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// A type reference is considered independent if each type argument is considered independent.
|
||||
function isIndependentTypeReference(node: TypeReferenceNode): boolean {
|
||||
if (node.typeArguments) {
|
||||
for (const typeNode of node.typeArguments) {
|
||||
if (!isIndependentType(typeNode)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
/** A type reference is free of this references if each type argument is free of this references. */
|
||||
function isTypeReferenceFreeOfThisReference(node: TypeReferenceNode): boolean {
|
||||
return !node.typeArguments || node.typeArguments.every(isTypeFreeOfThisReference);
|
||||
}
|
||||
|
||||
// A type is considered independent if it the any, string, number, boolean, symbol, or void keyword, a string
|
||||
// literal type, an array with an element type that is considered independent, or a type reference that is
|
||||
// considered independent.
|
||||
function isIndependentType(node: TypeNode): boolean {
|
||||
/**
|
||||
* A type is free of this references if it's the any, string, number, boolean, symbol, or void keyword, a string
|
||||
* literal type, an array with an element type that is free of this references, or a type reference that is
|
||||
* free of this references.
|
||||
*/
|
||||
function isTypeFreeOfThisReference(node: TypeNode): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.AnyKeyword:
|
||||
case SyntaxKind.StringKeyword:
|
||||
@@ -5357,54 +5356,58 @@ namespace ts {
|
||||
case SyntaxKind.LiteralType:
|
||||
return true;
|
||||
case SyntaxKind.ArrayType:
|
||||
return isIndependentType((<ArrayTypeNode>node).elementType);
|
||||
return isTypeFreeOfThisReference((<ArrayTypeNode>node).elementType);
|
||||
case SyntaxKind.TypeReference:
|
||||
return isIndependentTypeReference(<TypeReferenceNode>node);
|
||||
return isTypeReferenceFreeOfThisReference(<TypeReferenceNode>node);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// A variable-like declaration is considered independent (free of this references) if it has a type annotation
|
||||
// that specifies an independent type, or if it has no type annotation and no initializer (and thus of type any).
|
||||
function isIndependentVariableLikeDeclaration(node: VariableLikeDeclaration): boolean {
|
||||
/** A type parameter is this-free if its contraint is this-free, or if it has no constraint. */
|
||||
function isTypeParameterFreeOfThisReference(node: TypeParameterDeclaration) {
|
||||
return !node.constraint || isTypeFreeOfThisReference(node.constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable-like declaration is free of this references if it has a type annotation
|
||||
* that is this-free, or if it has no type annotation and no initializer (and is thus of type any).
|
||||
*/
|
||||
function isVariableLikeDeclarationFreeOfThisReference(node: VariableLikeDeclaration): boolean {
|
||||
const typeNode = getEffectiveTypeAnnotationNode(node);
|
||||
return typeNode ? isIndependentType(typeNode) : !node.initializer;
|
||||
return typeNode ? isTypeFreeOfThisReference(typeNode) : !node.initializer;
|
||||
}
|
||||
|
||||
// A function-like declaration is considered independent (free of this references) if it has a return type
|
||||
// annotation that is considered independent and if each parameter is considered independent.
|
||||
function isIndependentFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
|
||||
if (node.kind !== SyntaxKind.Constructor) {
|
||||
const typeNode = getEffectiveReturnTypeNode(node);
|
||||
if (!typeNode || !isIndependentType(typeNode)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const parameter of node.parameters) {
|
||||
if (!isIndependentVariableLikeDeclaration(parameter)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
/**
|
||||
* A function-like declaration is considered free of `this` references if it has a return type
|
||||
* annotation that is free of this references and if each parameter is this-free and if
|
||||
* each type parameter (if present) is this-free.
|
||||
*/
|
||||
function isFunctionLikeDeclarationFreeOfThisReference(node: FunctionLikeDeclaration): boolean {
|
||||
const returnType = getEffectiveReturnTypeNode(node);
|
||||
return (node.kind === SyntaxKind.Constructor || (returnType && isTypeFreeOfThisReference(returnType))) &&
|
||||
node.parameters.every(isVariableLikeDeclarationFreeOfThisReference) &&
|
||||
(!node.typeParameters || node.typeParameters.every(isTypeParameterFreeOfThisReference));
|
||||
}
|
||||
|
||||
// Returns true if the class or interface member given by the symbol is free of "this" references. The
|
||||
// function may return false for symbols that are actually free of "this" references because it is not
|
||||
// feasible to perform a complete analysis in all cases. In particular, property members with types
|
||||
// inferred from their initializers and function members with inferred return types are conservatively
|
||||
// assumed not to be free of "this" references.
|
||||
function isIndependentMember(symbol: Symbol): boolean {
|
||||
/**
|
||||
* Returns true if the class or interface member given by the symbol is free of "this" references. The
|
||||
* function may return false for symbols that are actually free of "this" references because it is not
|
||||
* feasible to perform a complete analysis in all cases. In particular, property members with types
|
||||
* inferred from their initializers and function members with inferred return types are conservatively
|
||||
* assumed not to be free of "this" references.
|
||||
*/
|
||||
function isFreeOfThisReference(symbol: Symbol): boolean {
|
||||
if (symbol.declarations && symbol.declarations.length === 1) {
|
||||
const declaration = symbol.declarations[0];
|
||||
if (declaration) {
|
||||
switch (declaration.kind) {
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
return isIndependentVariableLikeDeclaration(<VariableLikeDeclaration>declaration);
|
||||
return isVariableLikeDeclarationFreeOfThisReference(<VariableLikeDeclaration>declaration);
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.Constructor:
|
||||
return isIndependentFunctionLikeDeclaration(<FunctionLikeDeclaration>declaration);
|
||||
return isFunctionLikeDeclarationFreeOfThisReference(<FunctionLikeDeclaration>declaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5416,7 +5419,7 @@ namespace ts {
|
||||
function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable {
|
||||
const result = createSymbolTable();
|
||||
for (const symbol of symbols) {
|
||||
result.set(symbol.escapedName, mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper));
|
||||
result.set(symbol.escapedName, mappingThisOnly && isFreeOfThisReference(symbol) ? symbol : instantiateSymbol(symbol, mapper));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user