diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1c41be84ba..456f7bd07b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2156,6 +2156,11 @@ namespace ts { return false; } + function isTypeSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node): boolean { + const access = isSymbolAccessible(typeSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false); + return access.accessibility === SymbolAccessibility.Accessible; + } + /** * Check if the given symbol in given enclosing declaration is accessible and mark all associated alias to be visible if requested * @@ -2486,8 +2491,7 @@ namespace ts { // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. return createTypeReferenceNode(name, /*typeArguments*/ undefined); } - if (!inTypeAlias && type.aliasSymbol && - isSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { + if (!inTypeAlias && type.aliasSymbol && isTypeSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration)) { const name = symbolToTypeReferenceName(type.aliasSymbol); const typeArgumentNodes = mapToTypeNodes(type.aliasTypeArguments, context); return createTypeReferenceNode(name, typeArgumentNodes); @@ -3254,7 +3258,7 @@ namespace ts { buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags); } else if (!(flags & TypeFormatFlags.InTypeAlias) && type.aliasSymbol && - isSymbolAccessible(type.aliasSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { + ((flags & TypeFormatFlags.UseAliasDefinedOutsideCurrentScope) || isTypeSymbolAccessible(type.aliasSymbol, enclosingDeclaration))) { const typeArguments = type.aliasTypeArguments; writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, length(typeArguments), nextFlags); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2b3e5ab36d..f0db341d99 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2722,6 +2722,8 @@ namespace ts { AddUndefined = 1 << 13, // Add undefined to types of initialized, non-optional parameters WriteClassExpressionAsTypeLiteral = 1 << 14, // Write a type literal instead of (Anonymous class) InArrayType = 1 << 15, // Writing an array element type + UseAliasDefinedOutsideCurrentScope = 1 << 16, // For a `type T = ... ` defined in a different file, write `T` instead of its value, + // even though `T` can't be accessed in the current scope. } export const enum SymbolFormatFlags { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index a5b035154b..27215190db 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1261,6 +1261,7 @@ namespace ts { } export function signatureToDisplayParts(typechecker: TypeChecker, signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[] { + flags |= TypeFormatFlags.UseAliasDefinedOutsideCurrentScope; return mapToDisplayParts(writer => { typechecker.getSymbolDisplayBuilder().buildSignatureDisplay(signature, writer, enclosingDeclaration, flags); }); diff --git a/tests/cases/fourslash/quickInfoTypeAliasDefinedInDifferentFile.ts b/tests/cases/fourslash/quickInfoTypeAliasDefinedInDifferentFile.ts new file mode 100644 index 0000000000..2597b5e888 --- /dev/null +++ b/tests/cases/fourslash/quickInfoTypeAliasDefinedInDifferentFile.ts @@ -0,0 +1,11 @@ +/// + +// @Filename: /a.ts +////export type X = { x: number }; +////export function f(x: X): void {} + +// @Filename: /b.ts +////import { f } from "./a"; +/////**/f({ x: 1 }); + +verify.quickInfoAt("", "(alias) f(x: X): void\nimport f");