mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(compiler): extract call and construct signatures of interfaces (#54053)
This adds initial support for extracting and rendering call and construct signatures of classes, like within the new `InputFunction` for signal inputs. For now, signatures are a rare occasion and represented as class member entries. In the future we might consider exposing this via its own entry type, and field on the class/interface entry. PR Close #54053
This commit is contained in:
committed by
Jessica Janiuk
parent
58b8a232d6
commit
fe4343cf13
+1
-1
@@ -152,7 +152,7 @@
|
||||
"@actions/core": "^1.10.0",
|
||||
"@angular-devkit/architect-cli": "^0.1701.0-rc",
|
||||
"@angular/animations": "^17.1.0-next",
|
||||
"@angular/build-tooling": "https://github.com/angular/dev-infra-private-build-tooling-builds.git#8a32f9fd9af99832f337db9fe29842797121e96d",
|
||||
"@angular/build-tooling": "https://github.com/angular/dev-infra-private-build-tooling-builds.git#e97da496dc89481ef8e91433f2a1b674a2360340",
|
||||
"@angular/docs": "https://github.com/angular/dev-infra-private-docs-builds.git#1ff2479610f1f145433b95bbe85f6606e6e975c1",
|
||||
"@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#cdbbb64c01e02d4edda03d66fa0919504d8c5dfc",
|
||||
"@babel/helper-remap-async-to-generator": "^7.18.9",
|
||||
|
||||
@@ -30,13 +30,20 @@ type PropertyDeclarationLike = ts.PropertyDeclaration|ts.AccessorDeclaration;
|
||||
/** Type representing either a class declaration ro an interface declaration. */
|
||||
type ClassDeclarationLike = ts.ClassDeclaration|ts.InterfaceDeclaration;
|
||||
|
||||
/** Type representing either a class member node or an interface member node. */
|
||||
/** Type representing either a class or interface member. */
|
||||
type MemberElement = ts.ClassElement|ts.TypeElement;
|
||||
|
||||
/** Type representing either a class method declaration or an interface method signature. */
|
||||
/** Type representing a signature element of an interface. */
|
||||
type SignatureElement = ts.CallSignatureDeclaration|ts.ConstructSignatureDeclaration;
|
||||
|
||||
/**
|
||||
* Type representing either:
|
||||
*/
|
||||
type MethodLike = ts.MethodDeclaration|ts.MethodSignature;
|
||||
|
||||
/** Type representing either a class property declaration or an interface property signature. */
|
||||
/**
|
||||
* Type representing either a class property declaration or an interface property signature.
|
||||
*/
|
||||
type PropertyLike = PropertyDeclarationLike|ts.PropertySignature;
|
||||
|
||||
/** Extractor to pull info for API reference documentation for a TypeScript class or interface. */
|
||||
@@ -53,7 +60,7 @@ class ClassExtractor {
|
||||
isAbstract: this.isAbstract(),
|
||||
entryType: ts.isInterfaceDeclaration(this.declaration) ? EntryType.Interface :
|
||||
EntryType.UndecoratedClass,
|
||||
members: this.extractAllClassMembers(),
|
||||
members: this.extractSignatures().concat(this.extractAllClassMembers()),
|
||||
generics: extractGenerics(this.declaration),
|
||||
description: extractJsDocDescription(this.declaration),
|
||||
jsdocTags: extractJsDocTags(this.declaration),
|
||||
@@ -92,9 +99,15 @@ class ClassExtractor {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Extract docs for all call signatures in the current class/interface. */
|
||||
protected extractSignatures(): MemberEntry[] {
|
||||
return this.computeAllSignatureDeclarations().map(s => this.extractSignature(s));
|
||||
}
|
||||
|
||||
/** Extracts docs for a class method. */
|
||||
protected extractMethod(methodDeclaration: MethodLike): MethodEntry {
|
||||
const functionExtractor = new FunctionExtractor(methodDeclaration, this.typeChecker);
|
||||
const functionExtractor = new FunctionExtractor(
|
||||
methodDeclaration.name.getText(), methodDeclaration, this.typeChecker);
|
||||
return {
|
||||
...functionExtractor.extract(),
|
||||
memberType: MemberType.Method,
|
||||
@@ -102,6 +115,20 @@ class ClassExtractor {
|
||||
};
|
||||
}
|
||||
|
||||
/** Extracts docs for a signature element (usually inside an interface). */
|
||||
protected extractSignature(signature: SignatureElement): MethodEntry {
|
||||
// No name for the function if we are dealing with call signatures.
|
||||
// For construct signatures we are using `new` as the name of the function for now.
|
||||
// TODO: Consider exposing a new entry type for signature types.
|
||||
const functionExtractor = new FunctionExtractor(
|
||||
ts.isConstructSignatureDeclaration(signature) ? 'new' : '', signature, this.typeChecker);
|
||||
return {
|
||||
...functionExtractor.extract(),
|
||||
memberType: MemberType.Method,
|
||||
memberTags: [],
|
||||
};
|
||||
}
|
||||
|
||||
/** Extracts doc info for a property declaration. */
|
||||
protected extractClassProperty(propertyDeclaration: PropertyLike): PropertyEntry {
|
||||
return {
|
||||
@@ -137,16 +164,35 @@ class ClassExtractor {
|
||||
return tags;
|
||||
}
|
||||
|
||||
/** Computes all signature declarations of the class/interface. */
|
||||
private computeAllSignatureDeclarations(): SignatureElement[] {
|
||||
const type = this.typeChecker.getTypeAtLocation(this.declaration);
|
||||
const signatures = [
|
||||
...type.getCallSignatures(),
|
||||
...type.getConstructSignatures(),
|
||||
];
|
||||
|
||||
const result: SignatureElement[] = [];
|
||||
for (const signature of signatures) {
|
||||
const decl = signature.getDeclaration();
|
||||
if (this.isDocumentableSignature(decl) && this.isDocumentableMember(decl)) {
|
||||
result.push(decl);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Gets all member declarations, including inherited members. */
|
||||
private getMemberDeclarations(): MemberElement[] {
|
||||
// We rely on TypeScript to resolve all the inherited members to their
|
||||
// ultimate form via `getPropertiesOfType`. This is important because child
|
||||
// ultimate form via `getProperties`. This is important because child
|
||||
// classes may narrow types or add method overloads.
|
||||
const type = this.typeChecker.getTypeAtLocation(this.declaration);
|
||||
const members = type.getProperties();
|
||||
|
||||
// While the properties of the declaration type represent the properties that exist
|
||||
// on a clas *instance*, static members are properties on the class symbol itself.
|
||||
// on a class *instance*, static members are properties on the class symbol itself.
|
||||
const typeOfConstructor = this.typeChecker.getTypeOfSymbol(type.symbol);
|
||||
const staticMembers = typeOfConstructor.getProperties();
|
||||
|
||||
@@ -221,6 +267,13 @@ class ClassExtractor {
|
||||
return ts.isMethodDeclaration(member) || ts.isMethodSignature(member);
|
||||
}
|
||||
|
||||
/** Gets whether the given signature declaration is documentable. */
|
||||
private isDocumentableSignature(signature: ts.SignatureDeclaration):
|
||||
signature is SignatureElement {
|
||||
return ts.isConstructSignatureDeclaration(signature) ||
|
||||
ts.isCallSignatureDeclaration(signature);
|
||||
}
|
||||
|
||||
/** Gets whether the declaration for this extractor is abstract. */
|
||||
private isAbstract(): boolean {
|
||||
const modifiers = this.declaration.modifiers ?? [];
|
||||
|
||||
@@ -120,6 +120,7 @@ export interface FunctionEntry extends DocEntry {
|
||||
params: ParameterEntry[];
|
||||
returnType: string;
|
||||
generics: GenericEntry[];
|
||||
isNewType: boolean;
|
||||
}
|
||||
|
||||
/** Sub-entry for a single class or enum member. */
|
||||
|
||||
@@ -66,7 +66,8 @@ export class DocsExtractor {
|
||||
}
|
||||
|
||||
if (ts.isFunctionDeclaration(node)) {
|
||||
const functionExtractor = new FunctionExtractor(node, this.typeChecker);
|
||||
// Name is guaranteed to be set, because it's exported directly.
|
||||
const functionExtractor = new FunctionExtractor(node.name!.getText(), node, this.typeChecker);
|
||||
return functionExtractor.extract();
|
||||
}
|
||||
|
||||
@@ -107,7 +108,7 @@ export class DocsExtractor {
|
||||
for (let i = 0; i < declarationCount; i++) {
|
||||
const [exportName, declaration] = exportedDeclarations[i];
|
||||
if (ts.isFunctionDeclaration(declaration)) {
|
||||
const extractor = new FunctionExtractor(declaration, this.typeChecker);
|
||||
const extractor = new FunctionExtractor(exportName, declaration, this.typeChecker);
|
||||
const overloads = extractor.getOverloads().map(overload => [exportName, overload] as const);
|
||||
|
||||
exportedDeclarations.push(...overloads);
|
||||
|
||||
@@ -13,10 +13,13 @@ import {extractGenerics} from './generics_extractor';
|
||||
import {extractJsDocDescription, extractJsDocTags, extractRawJsDoc} from './jsdoc_extractor';
|
||||
import {extractResolvedTypeString} from './type_extractor';
|
||||
|
||||
export type FunctionLike = ts.FunctionDeclaration|ts.MethodDeclaration|ts.MethodSignature;
|
||||
export type FunctionLike = ts.FunctionDeclaration|ts.MethodDeclaration|ts.MethodSignature|
|
||||
ts.CallSignatureDeclaration|ts.ConstructSignatureDeclaration;
|
||||
|
||||
export class FunctionExtractor {
|
||||
constructor(private declaration: FunctionLike, private typeChecker: ts.TypeChecker) {}
|
||||
constructor(
|
||||
private name: string, private declaration: FunctionLike,
|
||||
private typeChecker: ts.TypeChecker) {}
|
||||
|
||||
extract(): FunctionEntry {
|
||||
// TODO: is there any real situation in which the signature would not be available here?
|
||||
@@ -28,9 +31,8 @@ export class FunctionExtractor {
|
||||
|
||||
return {
|
||||
params: this.extractAllParams(this.declaration.parameters),
|
||||
// We know that the function has a name here because we would have skipped it
|
||||
// already before getting to this point if it was anonymous.
|
||||
name: this.declaration.name!.getText(),
|
||||
name: this.name,
|
||||
isNewType: ts.isConstructSignatureDeclaration(this.declaration),
|
||||
returnType,
|
||||
entryType: EntryType.Function,
|
||||
generics: extractGenerics(this.declaration),
|
||||
|
||||
@@ -298,10 +298,9 @@
|
||||
"@angular/core" "^13.0.0 || ^14.0.0-0"
|
||||
reflect-metadata "^0.1.13"
|
||||
|
||||
"@angular/build-tooling@https://github.com/angular/dev-infra-private-build-tooling-builds.git#8a32f9fd9af99832f337db9fe29842797121e96d":
|
||||
version "0.0.0-ae9155d1083fd8390e98bfe09a5ccf89f0375b52"
|
||||
uid "8a32f9fd9af99832f337db9fe29842797121e96d"
|
||||
resolved "https://github.com/angular/dev-infra-private-build-tooling-builds.git#8a32f9fd9af99832f337db9fe29842797121e96d"
|
||||
"@angular/build-tooling@https://github.com/angular/dev-infra-private-build-tooling-builds.git#e97da496dc89481ef8e91433f2a1b674a2360340":
|
||||
version "0.0.0-f0fa701114b57b7d633a2ab3813034ab1735e3c0"
|
||||
resolved "https://github.com/angular/dev-infra-private-build-tooling-builds.git#e97da496dc89481ef8e91433f2a1b674a2360340"
|
||||
dependencies:
|
||||
"@angular-devkit/build-angular" "17.1.0-rc.1"
|
||||
"@angular/benchpress" "0.3.0"
|
||||
@@ -332,7 +331,7 @@
|
||||
marked-mangle "^1.1.4"
|
||||
preact "^10.17.1"
|
||||
preact-render-to-string "^6.2.1"
|
||||
prettier "3.2.2"
|
||||
prettier "3.2.4"
|
||||
protractor "^7.0.0"
|
||||
selenium-webdriver "4.16.0"
|
||||
send "^0.18.0"
|
||||
@@ -400,7 +399,6 @@
|
||||
|
||||
"@angular/docs@https://github.com/angular/dev-infra-private-docs-builds.git#1ff2479610f1f145433b95bbe85f6606e6e975c1":
|
||||
version "0.0.0-ae9155d1083fd8390e98bfe09a5ccf89f0375b52"
|
||||
uid "1ff2479610f1f145433b95bbe85f6606e6e975c1"
|
||||
resolved "https://github.com/angular/dev-infra-private-docs-builds.git#1ff2479610f1f145433b95bbe85f6606e6e975c1"
|
||||
dependencies:
|
||||
"@angular/cdk" "17.1.0-rc.0"
|
||||
@@ -488,7 +486,6 @@
|
||||
|
||||
"@angular/ng-dev@https://github.com/angular/dev-infra-private-ng-dev-builds.git#cdbbb64c01e02d4edda03d66fa0919504d8c5dfc":
|
||||
version "0.0.0-ae9155d1083fd8390e98bfe09a5ccf89f0375b52"
|
||||
uid cdbbb64c01e02d4edda03d66fa0919504d8c5dfc
|
||||
resolved "https://github.com/angular/dev-infra-private-ng-dev-builds.git#cdbbb64c01e02d4edda03d66fa0919504d8c5dfc"
|
||||
dependencies:
|
||||
"@yarnpkg/lockfile" "^1.1.0"
|
||||
@@ -13448,10 +13445,10 @@ prelude-ls@~1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
||||
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
|
||||
|
||||
prettier@3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.2.tgz#96e580f7ca9c96090ad054616c0c4597e2844b65"
|
||||
integrity sha512-HTByuKZzw7utPiDO523Tt2pLtEyK7OibUD9suEJQrPUCYQqrHr74GGX6VidMrovbf/I50mPqr8j/II6oBAuc5A==
|
||||
prettier@3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.4.tgz#4723cadeac2ce7c9227de758e5ff9b14e075f283"
|
||||
integrity sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==
|
||||
|
||||
prettier@^3.0.0:
|
||||
version "3.0.3"
|
||||
@@ -14589,6 +14586,7 @@ select-hose@^2.0.0:
|
||||
integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==
|
||||
|
||||
"selenium-webdriver4@npm:selenium-webdriver@4.16.0", selenium-webdriver@4.16.0:
|
||||
name selenium-webdriver4
|
||||
version "4.16.0"
|
||||
resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.16.0.tgz#2f1a2426d876aa389d1c937b00f034c2c7808360"
|
||||
integrity sha512-IbqpRpfGE7JDGgXHJeWuCqT/tUqnLvZ14csSwt+S8o4nJo3RtQoE9VR4jB47tP/A8ArkYsh/THuMY6kyRP6kuA==
|
||||
|
||||
Reference in New Issue
Block a user