From 1e2d4d646dc1bd9b94bd164bf2450e6bab1ab24f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 29 Dec 2024 21:29:38 +0100 Subject: [PATCH] Fixed inherited JSDoc lookup on static properties of classes with symbol-less base types --- src/compiler/checker.ts | 1 + src/compiler/types.ts | 1 + src/services/services.ts | 11 ++++-- ...InfoJsDocNoStaticVsInstanceMethodMixup1.ts | 26 ++++++++++++++ .../quickInfoJsDocOverridesWithMixin1.ts | 35 +++++++++++++++++++ 5 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/quickInfoJsDocNoStaticVsInstanceMethodMixup1.ts create mode 100644 tests/cases/fourslash/quickInfoJsDocOverridesWithMixin1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 80b61edd3657a..0a292e5029acc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1621,6 +1621,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getSymbolsOfParameterPropertyDeclaration(parameter, escapeLeadingUnderscores(parameterName)); }, getDeclaredTypeOfSymbol, + getBaseConstructorTypeOfClass, getPropertiesOfType, getPropertyOfType: (type, name) => getPropertyOfType(type, escapeLeadingUnderscores(name)), getPrivateIdentifierPropertyOfType: (leftType: Type, name: string, location: Node) => { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 49938f0fc8249..5d9d6954d9af4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5055,6 +5055,7 @@ export interface TypeChecker { getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type; getTypeOfSymbol(symbol: Symbol): Type; getDeclaredTypeOfSymbol(symbol: Symbol): Type; + /** @internal */ getBaseConstructorTypeOfClass(type: InterfaceType): Type; getPropertiesOfType(type: Type): Symbol[]; getPropertyOfType(type: Type, propertyName: string): Symbol | undefined; getPrivateIdentifierPropertyOfType(leftType: Type, name: string, location: Node): Symbol | undefined; diff --git a/src/services/services.ts b/src/services/services.ts index fb5e9a0857929..1e8562a8d39f2 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1051,11 +1051,16 @@ function findBaseOfDeclaration(checker: TypeChecker, declaration: Declaration const classOrInterfaceDeclaration = declaration.parent?.kind === SyntaxKind.Constructor ? declaration.parent.parent : declaration.parent; if (!classOrInterfaceDeclaration) return; - const isStaticMember = hasStaticModifier(declaration); + if (hasStaticModifier(declaration)) { + const classType = checker.getTypeAtLocation(classOrInterfaceDeclaration); + Debug.assert(classType.isClassOrInterface()); + const staticBaseType = checker.getBaseConstructorTypeOfClass(classType); + const symbol = checker.getPropertyOfType(staticBaseType, declaration.symbol.name); + return symbol ? cb(symbol) : undefined; + } return firstDefined(getAllSuperTypeNodes(classOrInterfaceDeclaration), superTypeNode => { const baseType = checker.getTypeAtLocation(superTypeNode); - const type = isStaticMember && baseType.symbol ? checker.getTypeOfSymbol(baseType.symbol) : baseType; - const symbol = checker.getPropertyOfType(type, declaration.symbol.name); + const symbol = checker.getPropertyOfType(baseType, declaration.symbol.name); return symbol ? cb(symbol) : undefined; }); } diff --git a/tests/cases/fourslash/quickInfoJsDocNoStaticVsInstanceMethodMixup1.ts b/tests/cases/fourslash/quickInfoJsDocNoStaticVsInstanceMethodMixup1.ts new file mode 100644 index 0000000000000..35e2921c2c652 --- /dev/null +++ b/tests/cases/fourslash/quickInfoJsDocNoStaticVsInstanceMethodMixup1.ts @@ -0,0 +1,26 @@ +/// + +//// declare class BaseClass { +//// /** some documentation for instance method */ +//// method(): string; +//// } +//// +//// type AnyConstructor = abstract new (...args: any[]) => object; +//// +//// function Mix(BaseClass: T) { +//// abstract class MixinClass extends BaseClass { +//// constructor(...args: any[]) { +//// super(...args); +//// } +//// } +//// +//// return MixinClass; +//// } +//// +//// declare class Mixed extends Mix(BaseClass) { +//// static method(): string; +//// } +//// +//// Mixed.method/*1*/; + +verify.quickInfoAt("1", "(method) Mixed.method(): string", undefined); \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoJsDocOverridesWithMixin1.ts b/tests/cases/fourslash/quickInfoJsDocOverridesWithMixin1.ts new file mode 100644 index 0000000000000..8794719dae44d --- /dev/null +++ b/tests/cases/fourslash/quickInfoJsDocOverridesWithMixin1.ts @@ -0,0 +1,35 @@ +/// + +//// declare class BaseClass { +//// /** some documentation for static method */ +//// static staticMethod(): number; +//// +//// /** some documentation for instance method */ +//// instanceMethod(): string; +//// } +//// +//// type AnyConstructor = abstract new (...args: any[]) => object; +//// +//// function Mix(BaseClass: T) { +//// abstract class MixinClass extends BaseClass { +//// constructor(...args: any[]) { +//// super(...args); +//// } +//// } +//// +//// return MixinClass; +//// } +//// +//// declare class Mixed extends Mix(BaseClass) { +//// static staticMethod(): number; +//// +//// instanceMethod(): string; +//// } +//// +//// Mixed.staticMethod/*1*/; +//// +//// const m = new Mixed(); +//// m.instanceMethod/*2*/; + +verify.quickInfoAt("1", "(method) Mixed.staticMethod(): number", "some documentation for static method"); +verify.quickInfoAt("2", "(method) Mixed.instanceMethod(): string", "some documentation for instance method"); \ No newline at end of file