Skip to content

Commit 1cb7cbd

Browse files
committed
Ensure parameters are in scope when converting parameter/return types into type nodes
1 parent 71ad90d commit 1cb7cbd

File tree

1 file changed

+35
-3
lines changed

1 file changed

+35
-3
lines changed

src/compiler/checker.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5042,7 +5042,7 @@ namespace ts {
50425042
}
50435043
if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams &&
50445044
type.flags & TypeFlags.TypeParameter &&
5045-
!isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration)) {
5045+
typeParameterIsCachedOrNotAccessible(type, context)) {
50465046
const name = typeParameterToName(type, context);
50475047
context.approximateLength += idText(name).length;
50485048
return factory.createTypeReferenceNode(factory.createIdentifier(idText(name)), /*typeArguments*/ undefined);
@@ -5826,6 +5826,22 @@ namespace ts {
58265826
typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, context));
58275827
}
58285828

5829+
// For regular function/method declarations, the enclosing declaration will already be signature.declaration,
5830+
// so this is a no-op, but for arrow functions and function expressions, the enclosing declaration will be
5831+
// the declaration that the arrow function / function expression is assigned to, which is not the right scope.
5832+
//
5833+
// If the parameters or return type include "typeof globalThis.paramName", using the wrong scope will lead
5834+
// us to believe that we can emit "typeof paramName" instead, even though that would refer to the parameter,
5835+
// not the global. Make sure we are in the right scope by changing the enclosingDeclaration to the function.
5836+
//
5837+
// We check that this signature's declaration is within the existing enclosing declaration so we don't pull
5838+
// in types from other files; if it is outside of the enclosing declaration, then we shouldn't be able to
5839+
// access those parameter types via typeof anyway.
5840+
const saveEnclosingDeclaration = context.enclosingDeclaration;
5841+
if (signature.declaration && saveEnclosingDeclaration && findAncestor(signature.declaration, (n) => n === saveEnclosingDeclaration)) {
5842+
context.enclosingDeclaration = signature.declaration;
5843+
}
5844+
58295845
const expandedParams = getExpandedParameters(signature, /*skipUnionExpanding*/ true)[0];
58305846
// If the expanded parameter list had a variadic in a non-trailing position, don't expand it
58315847
const parameters = (some(expandedParams, p => p !== expandedParams[expandedParams.length - 1] && !!(getCheckFlags(p) & CheckFlags.RestParameter)) ? signature.parameters : expandedParams).map(parameter => symbolToParameterDeclaration(parameter, context, kind === SyntaxKind.Constructor, options?.privateSymbolVisitor, options?.bundledImports));
@@ -5882,6 +5898,7 @@ namespace ts {
58825898
node.typeArguments = factory.createNodeArray(typeArguments);
58835899
}
58845900

5901+
context.enclosingDeclaration = saveEnclosingDeclaration;
58855902
return node;
58865903
}
58875904

@@ -6366,13 +6383,28 @@ namespace ts {
63666383
return false;
63676384
}
63686385

6369-
function typeParameterToName(type: TypeParameter, context: NodeBuilderContext) {
6386+
// Returns true if this type parameter should be converted to a name via typeParameterToName,
6387+
// either because it is not accessible, or because it's a use of a type variable that has
6388+
// already been rewritten by typeParameterToName and so needs that rewritten name.
6389+
function typeParameterIsCachedOrNotAccessible(type: TypeParameter, context: NodeBuilderContext) {
6390+
return !!getCachedTypeParameterName(type, context) || !isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration);
6391+
}
6392+
6393+
function getCachedTypeParameterName(type: TypeParameter, context: NodeBuilderContext) {
63706394
if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && context.typeParameterNames) {
63716395
const cached = context.typeParameterNames.get(getTypeId(type));
63726396
if (cached) {
63736397
return cached;
63746398
}
63756399
}
6400+
return undefined;
6401+
}
6402+
6403+
function typeParameterToName(type: TypeParameter, context: NodeBuilderContext) {
6404+
const cached = getCachedTypeParameterName(type, context);
6405+
if (cached) {
6406+
return cached;
6407+
}
63766408
let result = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true);
63776409
if (!(result.kind & SyntaxKind.Identifier)) {
63786410
return factory.createIdentifier("(Missing type parameter)");
@@ -6630,7 +6662,7 @@ namespace ts {
66306662
}
66316663
if (isIdentifier(node)) {
66326664
const type = getDeclaredTypeOfSymbol(sym);
6633-
const name = sym.flags & SymbolFlags.TypeParameter && !isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration) ? typeParameterToName(type, context) : factory.cloneNode(node);
6665+
const name = sym.flags & SymbolFlags.TypeParameter && typeParameterIsCachedOrNotAccessible(type, context) ? typeParameterToName(type, context) : factory.cloneNode(node);
66346666
name.symbol = sym; // for quickinfo, which uses identifier symbol information
66356667
return { introducesError, node: setEmitFlags(setOriginalNode(name, node), EmitFlags.NoAsciiEscaping) };
66366668
}

0 commit comments

Comments
 (0)