@@ -7276,6 +7276,82 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
7276
7276
}
7277
7277
7278
7278
const expandedParams = getExpandedParameters(signature, /*skipUnionExpanding*/ true)[0];
7279
+
7280
+ // For regular function/method declarations, the enclosing declaration will already be signature.declaration,
7281
+ // so this is a no-op, but for arrow functions and function expressions, the enclosing declaration will be
7282
+ // the declaration that the arrow function / function expression is assigned to.
7283
+ //
7284
+ // If the parameters or return type include "typeof globalThis.paramName", using the wrong scope will lead
7285
+ // us to believe that we can emit "typeof paramName" instead, even though that would refer to the parameter,
7286
+ // not the global. Make sure we are in the right scope by changing the enclosingDeclaration to the function.
7287
+ //
7288
+ // We can't use the declaration directly; it may be in another file and so we may lose access to symbols
7289
+ // accessible to the current enclosing declaration, or gain access to symbols not accessible to the current
7290
+ // enclosing declaration. To keep this chain accurate, insert a fake scope into the chain which makes the
7291
+ // function's parameters visible.
7292
+ //
7293
+ // If the declaration is in a JS file, then we don't need to do this at all, as there are no annotations besides
7294
+ // JSDoc, which are always outside the function declaration, so are not in the parameter scope.
7295
+ let cleanup: (() => void) | undefined;
7296
+ if (
7297
+ context.enclosingDeclaration
7298
+ && signature.declaration
7299
+ && signature.declaration !== context.enclosingDeclaration
7300
+ && !isInJSFile(signature.declaration)
7301
+ && some(expandedParams)
7302
+ ) {
7303
+ // As a performance optimization, share the symbol table between all faked scopes
7304
+ // in this node builder chain. This is especially needed when we are working on an
7305
+ // excessively deep type; if we don't do this, then we spend all of our time adding
7306
+ // more and more scopes that need to be searched in isSymbolAccessible later. Since
7307
+ // all we really want to do is to mark certain names as unavailable, we can just
7308
+ // keep all of the names we're introducing in one large table and push/pop from it as
7309
+ // needed; isSymbolAccessible will walk upward and find the closest "fake" scope,
7310
+ // which will conveniently report on any and all faked scopes in the chain.
7311
+ //
7312
+ // It'd likely be better to store this somewhere else for isSymbolAccessible, but
7313
+ // since that API _only_ uses the enclosing declaration (and its parents), this is
7314
+ // seems like the best way to inject names into that search process.
7315
+ const existingFakeScope = findAncestor(context.enclosingDeclaration, node => !!getNodeLinks(node).fakeScopeForSignatureDeclaration);
7316
+ Debug.assertOptionalNode(existingFakeScope, isBlock);
7317
+
7318
+ const locals = existingFakeScope?.locals ?? createSymbolTable();
7319
+
7320
+ let newLocals: __String[] | undefined;
7321
+ for (const param of expandedParams) {
7322
+ if (!locals.has(param.escapedName)) {
7323
+ newLocals = append(newLocals, param.escapedName);
7324
+ locals.set(param.escapedName, param);
7325
+ }
7326
+ }
7327
+
7328
+ if (newLocals) {
7329
+ function removeNewLocals() {
7330
+ forEach(newLocals, s => locals.delete(s));
7331
+ }
7332
+
7333
+ if (existingFakeScope) {
7334
+ cleanup = removeNewLocals;
7335
+ }
7336
+ else {
7337
+ // Use a Block for this; the type of the node doesn't matter so long as it
7338
+ // has locals, and this is cheaper/easier than using a function-ish Node.
7339
+ const fakeScope = parseNodeFactory.createBlock(emptyArray);
7340
+ getNodeLinks(fakeScope).fakeScopeForSignatureDeclaration = true;
7341
+ fakeScope.locals = locals;
7342
+
7343
+ const saveEnclosingDeclaration = context.enclosingDeclaration;
7344
+ setParent(fakeScope, saveEnclosingDeclaration);
7345
+ context.enclosingDeclaration = fakeScope;
7346
+
7347
+ cleanup = () => {
7348
+ context.enclosingDeclaration = saveEnclosingDeclaration;
7349
+ removeNewLocals();
7350
+ };
7351
+ }
7352
+ }
7353
+ }
7354
+
7279
7355
// If the expanded parameter list had a variadic in a non-trailing position, don't expand it
7280
7356
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));
7281
7357
const thisParameter = context.flags & NodeBuilderFlags.OmitThisParameter ? undefined : tryGetThisParameterDeclaration(signature, context);
@@ -7331,6 +7407,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
7331
7407
node.typeArguments = factory.createNodeArray(typeArguments);
7332
7408
}
7333
7409
7410
+ cleanup?.();
7334
7411
return node;
7335
7412
}
7336
7413
@@ -7996,13 +8073,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
7996
8073
return !(getObjectFlags(type) & ObjectFlags.Reference) || !isTypeReferenceNode(existing) || length(existing.typeArguments) >= getMinTypeArgumentCount((type as TypeReference).target.typeParameters);
7997
8074
}
7998
8075
8076
+ function getEnclosingDeclarationIgnoringFakeScope(enclosingDeclaration: Node) {
8077
+ return findAncestor(enclosingDeclaration, n => !getNodeLinks(n).fakeScopeForSignatureDeclaration);
8078
+ }
8079
+
7999
8080
/**
8000
8081
* Unlike `typeToTypeNodeHelper`, this handles setting up the `AllowUniqueESSymbolType` flag
8001
8082
* so a `unique symbol` is returned when appropriate for the input symbol, rather than `typeof sym`
8002
8083
*/
8003
8084
function serializeTypeForDeclaration(context: NodeBuilderContext, type: Type, symbol: Symbol, enclosingDeclaration: Node | undefined, includePrivateSymbol?: (s: Symbol) => void, bundled?: boolean) {
8004
8085
if (!isErrorType(type) && enclosingDeclaration) {
8005
- const declWithExistingAnnotation = getDeclarationWithTypeAnnotation(symbol, enclosingDeclaration);
8086
+ const declWithExistingAnnotation = getDeclarationWithTypeAnnotation(symbol, getEnclosingDeclarationIgnoringFakeScope( enclosingDeclaration) );
8006
8087
if (declWithExistingAnnotation && !isFunctionLikeDeclaration(declWithExistingAnnotation) && !isGetAccessorDeclaration(declWithExistingAnnotation)) {
8007
8088
// try to reuse the existing annotation
8008
8089
const existing = getEffectiveTypeAnnotationNode(declWithExistingAnnotation)!;
@@ -8038,7 +8119,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
8038
8119
function serializeReturnTypeForSignature(context: NodeBuilderContext, type: Type, signature: Signature, includePrivateSymbol?: (s: Symbol) => void, bundled?: boolean) {
8039
8120
if (!isErrorType(type) && context.enclosingDeclaration) {
8040
8121
const annotation = signature.declaration && getEffectiveReturnTypeNode(signature.declaration);
8041
- if (!!findAncestor(annotation, n => n === context.enclosingDeclaration) && annotation) {
8122
+ const enclosingDeclarationIgnoringFakeScope = getEnclosingDeclarationIgnoringFakeScope(context.enclosingDeclaration);
8123
+ if (!!findAncestor(annotation, n => n === enclosingDeclarationIgnoringFakeScope) && annotation) {
8042
8124
const annotated = getTypeFromTypeNode(annotation);
8043
8125
const thisInstantiated = annotated.flags & TypeFlags.TypeParameter && (annotated as TypeParameter).isThisType ? instantiateType(annotated, signature.mapper) : annotated;
8044
8126
if (thisInstantiated === type && existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(annotation, type)) {
0 commit comments