@@ -53,7 +53,7 @@ class ClassExtractor {
5353 isAbstract : this . isAbstract ( ) ,
5454 entryType : ts . isInterfaceDeclaration ( this . declaration ) ? EntryType . Interface :
5555 EntryType . UndecoratedClass ,
56- members : this . extractAllClassMembers ( this . declaration ) ,
56+ members : this . extractAllClassMembers ( ) ,
5757 generics : extractGenerics ( this . declaration ) ,
5858 description : extractJsDocDescription ( this . declaration ) ,
5959 jsdocTags : extractJsDocTags ( this . declaration ) ,
@@ -62,10 +62,10 @@ class ClassExtractor {
6262 }
6363
6464 /** Extracts doc info for a class's members. */
65- protected extractAllClassMembers ( classDeclaration : ClassDeclarationLike ) : MemberEntry [ ] {
65+ protected extractAllClassMembers ( ) : MemberEntry [ ] {
6666 const members : MemberEntry [ ] = [ ] ;
6767
68- for ( const member of classDeclaration . members ) {
68+ for ( const member of this . getMemberDeclarations ( ) ) {
6969 if ( this . isMemberExcluded ( member ) ) continue ;
7070
7171 const memberEntry = this . extractClassMember ( member ) ;
@@ -130,9 +130,40 @@ class ClassExtractor {
130130 tags . push ( MemberTags . Optional ) ;
131131 }
132132
133+ if ( member . parent !== this . declaration ) {
134+ tags . push ( MemberTags . Inherited ) ;
135+ }
136+
133137 return tags ;
134138 }
135139
140+ /** Gets all member declarations, including inherited members. */
141+ private getMemberDeclarations ( ) : MemberElement [ ] {
142+ // We rely on TypeScript to resolve all the inherited members to their
143+ // ultimate form via `getPropertiesOfType`. This is important because child
144+ // classes may narrow types or add method overloads.
145+ const type = this . typeChecker . getTypeAtLocation ( this . declaration ) ;
146+ const members = type . getProperties ( ) ;
147+
148+ // While the properties of the declaration type represent the properties that exist
149+ // on a clas *instance*, static members are properties on the class symbol itself.
150+ const typeOfConstructor = this . typeChecker . getTypeOfSymbol ( type . symbol ) ;
151+ const staticMembers = typeOfConstructor . getProperties ( ) ;
152+
153+ const result : MemberElement [ ] = [ ] ;
154+ for ( const member of [ ...members , ...staticMembers ] ) {
155+ // A member may have multiple declarations in the case of function overloads.
156+ const memberDeclarations = member . getDeclarations ( ) ?? [ ] ;
157+ for ( const memberDeclaration of memberDeclarations ) {
158+ if ( this . isDocumentableMember ( memberDeclaration ) ) {
159+ result . push ( memberDeclaration ) ;
160+ }
161+ }
162+ }
163+
164+ return result ;
165+ }
166+
136167 /** Get the tags for a member that come from the declaration modifiers. */
137168 private getMemberTagsFromModifiers ( mods : Iterable < ts . ModifierLike > ) : MemberTags [ ] {
138169 const tags : MemberTags [ ] = [ ] ;
@@ -170,22 +201,22 @@ class ClassExtractor {
170201 private isMemberExcluded ( member : MemberElement ) : boolean {
171202 return ! member . name || ! this . isDocumentableMember ( member ) ||
172203 ! ! member . modifiers ?. some ( mod => mod . kind === ts . SyntaxKind . PrivateKeyword ) ||
173- isAngularPrivateName ( member . name . getText ( ) ) ;
204+ member . name . getText ( ) === 'prototype' || isAngularPrivateName ( member . name . getText ( ) ) ;
174205 }
175206
176207 /** Gets whether a class member is a method, property, or accessor. */
177- private isDocumentableMember ( member : MemberElement ) : member is MethodLike | PropertyLike {
208+ private isDocumentableMember ( member : ts . Node ) : member is MethodLike | PropertyLike {
178209 return this . isMethod ( member ) || this . isProperty ( member ) || ts . isAccessor ( member ) ;
179210 }
180211
181212 /** Gets whether a member is a property. */
182- private isProperty ( member : MemberElement ) : member is PropertyLike {
213+ private isProperty ( member : ts . Node ) : member is PropertyLike {
183214 // Classes have declarations, interface have signatures
184215 return ts . isPropertyDeclaration ( member ) || ts . isPropertySignature ( member ) ;
185216 }
186217
187218 /** Gets whether a member is a method. */
188- private isMethod ( member : MemberElement ) : member is MethodLike {
219+ private isMethod ( member : ts . Node ) : member is MethodLike {
189220 // Classes have declarations, interface have signatures
190221 return ts . isMethodDeclaration ( member ) || ts . isMethodSignature ( member ) ;
191222 }
@@ -197,20 +228,14 @@ class ClassExtractor {
197228 }
198229
199230 /** Gets whether a method is the concrete implementation for an overloaded function. */
200- private isImplementationForOverload ( method : MethodLike ) : boolean {
231+ private isImplementationForOverload ( method : MethodLike ) : boolean | undefined {
201232 // Method signatures (in an interface) are never implementations.
202233 if ( method . kind === ts . SyntaxKind . MethodSignature ) return false ;
203234
204- const methodsWithSameName =
205- this . declaration . members . filter ( member => member . name ?. getText ( ) === method . name . getText ( ) )
206- . sort ( ( a , b ) => a . pos - b . pos ) ;
207-
208- // No overloads.
209- if ( methodsWithSameName . length === 1 ) return false ;
210-
211- // The implementation is always the last declaration, so we know this is the
212- // implementation if it's the last position.
213- return method . pos === methodsWithSameName [ methodsWithSameName . length - 1 ] . pos ;
235+ const signature = this . typeChecker . getSignatureFromDeclaration ( method ) ;
236+ return signature &&
237+ this . typeChecker . isImplementationOfOverload (
238+ signature . declaration as ts . SignatureDeclaration ) ;
214239 }
215240}
216241
0 commit comments