build: ts-api-guardian should support interface with value types (#27223)
This fixes an issue where a value would hide the type.
```
export interface Foo {
someMethod(): void;
}
export const Foo: Function = ...;
```
In the above example the `Foo` constant will hide the `interface Foo` symbol.
This change properly saves the interface in addition to the type.
PR Close #27223
This commit is contained in:
committed by
Jason Aden
parent
23b06af940
commit
60e403bf6d
+100
-4
@@ -47,6 +47,10 @@ export declare function asNativeElements(debugEls: DebugElement[]): any;
|
||||
|
||||
export declare function assertPlatform(requiredToken: any): PlatformRef;
|
||||
|
||||
export interface Attribute {
|
||||
attributeName?: string;
|
||||
}
|
||||
|
||||
export declare const Attribute: AttributeDecorator;
|
||||
|
||||
export declare enum ChangeDetectionStrategy {
|
||||
@@ -95,6 +99,21 @@ export declare type CompilerOptions = {
|
||||
preserveWhitespaces?: boolean;
|
||||
};
|
||||
|
||||
export interface Component extends Directive {
|
||||
animations?: any[];
|
||||
changeDetection?: ChangeDetectionStrategy;
|
||||
encapsulation?: ViewEncapsulation;
|
||||
entryComponents?: Array<Type<any> | any[]>;
|
||||
interpolation?: [string, string];
|
||||
moduleId?: string;
|
||||
preserveWhitespaces?: boolean;
|
||||
styleUrls?: string[];
|
||||
styles?: string[];
|
||||
template?: string;
|
||||
templateUrl?: string;
|
||||
viewProviders?: Provider[];
|
||||
}
|
||||
|
||||
export declare const Component: ComponentDecorator;
|
||||
|
||||
export interface ComponentDecorator {
|
||||
@@ -137,7 +156,7 @@ export interface ConstructorSansProvider {
|
||||
deps?: any[];
|
||||
}
|
||||
|
||||
export declare const ContentChild: ContentChildDecorator;
|
||||
export declare type ContentChild = Query;
|
||||
|
||||
export interface ContentChildDecorator {
|
||||
(selector: Type<any> | Function | string, opts?: {
|
||||
@@ -148,7 +167,7 @@ export interface ContentChildDecorator {
|
||||
}): ContentChild;
|
||||
}
|
||||
|
||||
export declare const ContentChildren: ContentChildrenDecorator;
|
||||
export declare type ContentChildren = Query;
|
||||
|
||||
export interface ContentChildrenDecorator {
|
||||
(selector: Type<any> | Function | string, opts?: {
|
||||
@@ -242,6 +261,21 @@ export declare function defineInjector(options: {
|
||||
|
||||
export declare function destroyPlatform(): void;
|
||||
|
||||
export interface Directive {
|
||||
exportAs?: string;
|
||||
host?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
inputs?: string[];
|
||||
jit?: true;
|
||||
outputs?: string[];
|
||||
providers?: Provider[];
|
||||
queries?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
selector?: string;
|
||||
}
|
||||
|
||||
export declare const Directive: DirectiveDecorator;
|
||||
|
||||
export interface DirectiveDecorator {
|
||||
@@ -307,8 +341,15 @@ export interface GetTestability {
|
||||
findTestabilityInTree(registry: TestabilityRegistry, elem: any, findInAncestors: boolean): Testability | null;
|
||||
}
|
||||
|
||||
export interface Host {
|
||||
}
|
||||
|
||||
export declare const Host: HostDecorator;
|
||||
|
||||
export interface HostBinding {
|
||||
hostPropertyName?: string;
|
||||
}
|
||||
|
||||
export declare const HostBinding: HostBindingDecorator;
|
||||
|
||||
export interface HostDecorator {
|
||||
@@ -316,13 +357,26 @@ export interface HostDecorator {
|
||||
new (): Host;
|
||||
}
|
||||
|
||||
export interface HostListener {
|
||||
args?: string[];
|
||||
eventName?: string;
|
||||
}
|
||||
|
||||
export declare const HostListener: HostListenerDecorator;
|
||||
|
||||
export declare function inject<T>(token: Type<T> | InjectionToken<T>): T;
|
||||
export declare function inject<T>(token: Type<T> | InjectionToken<T>, flags?: InjectFlags): T | null;
|
||||
|
||||
export interface Inject {
|
||||
token: any;
|
||||
}
|
||||
|
||||
export declare const Inject: InjectDecorator;
|
||||
|
||||
export interface Injectable {
|
||||
providedIn?: Type<any> | 'root' | null;
|
||||
}
|
||||
|
||||
export declare const Injectable: InjectableDecorator;
|
||||
|
||||
export interface InjectableDecorator {
|
||||
@@ -385,6 +439,10 @@ export interface InjectorType<T> extends Type<T> {
|
||||
ngInjectorDef: never;
|
||||
}
|
||||
|
||||
export interface Input {
|
||||
bindingPropertyName?: string;
|
||||
}
|
||||
|
||||
export declare const Input: InputDecorator;
|
||||
|
||||
export declare function isDevMode(): boolean;
|
||||
@@ -480,6 +538,18 @@ export interface ModuleWithProviders<T = any /** TODO(alxhub): remove default wh
|
||||
|
||||
export declare type NgIterable<T> = Array<T> | Iterable<T>;
|
||||
|
||||
export interface NgModule {
|
||||
bootstrap?: Array<Type<any> | any[]>;
|
||||
declarations?: Array<Type<any> | any[]>;
|
||||
entryComponents?: Array<Type<any> | any[]>;
|
||||
exports?: Array<Type<any> | any[]>;
|
||||
id?: string;
|
||||
imports?: Array<Type<any> | ModuleWithProviders<{}> | any[]>;
|
||||
jit?: true;
|
||||
providers?: Provider[];
|
||||
schemas?: Array<SchemaMetadata | any[]>;
|
||||
}
|
||||
|
||||
export declare const NgModule: NgModuleDecorator;
|
||||
|
||||
export declare abstract class NgModuleFactory<T> {
|
||||
@@ -539,6 +609,9 @@ export interface OnInit {
|
||||
ngOnInit(): void;
|
||||
}
|
||||
|
||||
export interface Optional {
|
||||
}
|
||||
|
||||
export declare const Optional: OptionalDecorator;
|
||||
|
||||
export interface OptionalDecorator {
|
||||
@@ -546,10 +619,19 @@ export interface OptionalDecorator {
|
||||
new (): Optional;
|
||||
}
|
||||
|
||||
export interface Output {
|
||||
bindingPropertyName?: string;
|
||||
}
|
||||
|
||||
export declare const Output: OutputDecorator;
|
||||
|
||||
export declare const PACKAGE_ROOT_URL: InjectionToken<string>;
|
||||
|
||||
export interface Pipe {
|
||||
name: string;
|
||||
pure?: boolean;
|
||||
}
|
||||
|
||||
export declare const Pipe: PipeDecorator;
|
||||
|
||||
export interface PipeTransform {
|
||||
@@ -577,6 +659,14 @@ export interface Predicate<T> {
|
||||
|
||||
export declare type Provider = TypeProvider | ValueProvider | ClassProvider | ConstructorProvider | ExistingProvider | FactoryProvider | any[];
|
||||
|
||||
export interface Query {
|
||||
descendants: boolean;
|
||||
first: boolean;
|
||||
isViewQuery: boolean;
|
||||
read: any;
|
||||
selector: any;
|
||||
}
|
||||
|
||||
export declare abstract class Query {
|
||||
}
|
||||
|
||||
@@ -742,6 +832,9 @@ export declare enum SecurityContext {
|
||||
RESOURCE_URL = 5
|
||||
}
|
||||
|
||||
export interface Self {
|
||||
}
|
||||
|
||||
export declare const Self: SelfDecorator;
|
||||
|
||||
export interface SelfDecorator {
|
||||
@@ -763,6 +856,9 @@ export interface SimpleChanges {
|
||||
[propName: string]: SimpleChange;
|
||||
}
|
||||
|
||||
export interface SkipSelf {
|
||||
}
|
||||
|
||||
export declare const SkipSelf: SkipSelfDecorator;
|
||||
|
||||
export interface SkipSelfDecorator {
|
||||
@@ -841,7 +937,7 @@ export declare class Version {
|
||||
|
||||
export declare const VERSION: Version;
|
||||
|
||||
export declare const ViewChild: ViewChildDecorator;
|
||||
export declare type ViewChild = Query;
|
||||
|
||||
export interface ViewChildDecorator {
|
||||
(selector: Type<any> | Function | string, opts?: {
|
||||
@@ -852,7 +948,7 @@ export interface ViewChildDecorator {
|
||||
}): ViewChild;
|
||||
}
|
||||
|
||||
export declare const ViewChildren: ViewChildrenDecorator;
|
||||
export declare type ViewChildren = Query;
|
||||
|
||||
export interface ViewChildrenDecorator {
|
||||
(selector: Type<any> | Function | string, opts?: {
|
||||
|
||||
+46
@@ -47,6 +47,52 @@ export declare type MetadataOverride<T> = {
|
||||
|
||||
export declare function resetFakeAsyncZone(): void;
|
||||
|
||||
export interface TestBed {
|
||||
ngModule: Type<any> | Type<any>[];
|
||||
platform: PlatformRef;
|
||||
compileComponents(): Promise<any>;
|
||||
configureCompiler(config: {
|
||||
providers?: any[];
|
||||
useJit?: boolean;
|
||||
}): void;
|
||||
configureTestingModule(moduleDef: TestModuleMetadata): void;
|
||||
createComponent<T>(component: Type<T>): ComponentFixture<T>;
|
||||
deprecatedOverrideProvider(token: any, provider: {
|
||||
useFactory?: Function;
|
||||
useValue?: any;
|
||||
deps?: any[];
|
||||
}): void;
|
||||
/** @deprecated */ deprecatedOverrideProvider(token: any, provider: {
|
||||
useFactory: Function;
|
||||
deps: any[];
|
||||
}): void;
|
||||
deprecatedOverrideProvider(token: any, provider: {
|
||||
useValue: any;
|
||||
}): void;
|
||||
execute(tokens: any[], fn: Function, context?: any): any;
|
||||
get(token: any, notFoundValue?: any): any;
|
||||
initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): void;
|
||||
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void;
|
||||
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void;
|
||||
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void;
|
||||
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void;
|
||||
overrideProvider(token: any, provider: {
|
||||
useFactory?: Function;
|
||||
useValue?: any;
|
||||
deps?: any[];
|
||||
}): void;
|
||||
overrideProvider(token: any, provider: {
|
||||
useFactory: Function;
|
||||
deps: any[];
|
||||
}): void;
|
||||
overrideProvider(token: any, provider: {
|
||||
useValue: any;
|
||||
}): void;
|
||||
overrideTemplateUsingTestingModule(component: Type<any>, template: string): void;
|
||||
resetTestEnvironment(): void;
|
||||
resetTestingModule(): void;
|
||||
}
|
||||
|
||||
export declare const TestBed: TestBedStatic;
|
||||
|
||||
export interface TestBedStatic {
|
||||
|
||||
@@ -115,7 +115,7 @@ class ResolvedDeclarationEmitter {
|
||||
throw new Error(`Source file "${this.fileName}" not found`);
|
||||
}
|
||||
|
||||
let output = '';
|
||||
let output: string[] = [];
|
||||
|
||||
const resolvedSymbols = this.getResolvedSymbols(sourceFile);
|
||||
// Sort all symbols so that the output is more deterministic
|
||||
@@ -126,42 +126,19 @@ class ResolvedDeclarationEmitter {
|
||||
continue;
|
||||
}
|
||||
|
||||
let decl: ts.Node|undefined =
|
||||
symbol.valueDeclaration || symbol.declarations && symbol.declarations[0];
|
||||
if (!decl) {
|
||||
const typeDecl = symbol.declarations && symbol.declarations[0];
|
||||
const valDecl = symbol.valueDeclaration;
|
||||
if (!typeDecl && !valDecl) {
|
||||
this.diagnostics.push({
|
||||
type: 'warn',
|
||||
message: `${sourceFile.fileName}: error: No declaration found for symbol "${symbol.name}"`
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// The declaration node may not be a complete statement, e.g. for var/const
|
||||
// symbols. We need to find the complete export statement by traversing
|
||||
// upwards.
|
||||
while (!hasModifier(decl, ts.SyntaxKind.ExportKeyword) && decl.parent) {
|
||||
decl = decl.parent;
|
||||
}
|
||||
|
||||
if (hasModifier(decl, ts.SyntaxKind.ExportKeyword)) {
|
||||
// Make an empty line between two exports
|
||||
if (output) {
|
||||
output += '\n';
|
||||
}
|
||||
|
||||
const jsdocComment = this.processJsDocTags(decl, this.options.exportTags);
|
||||
if (jsdocComment) {
|
||||
output += jsdocComment + '\n';
|
||||
}
|
||||
|
||||
output += stripEmptyLines(this.emitNode(decl)) + '\n';
|
||||
} else {
|
||||
// This may happen for symbols re-exported from external modules.
|
||||
this.diagnostics.push({
|
||||
type: 'warn',
|
||||
message:
|
||||
createErrorMessage(decl, `No export declaration found for symbol "${symbol.name}"`)
|
||||
});
|
||||
typeDecl && this.emitDeclaration(symbol, typeDecl, output);
|
||||
if (valDecl && typeDecl.kind === ts.SyntaxKind.InterfaceDeclaration) {
|
||||
// Only generate value declarations in case of interfaces.
|
||||
valDecl && this.emitDeclaration(symbol, valDecl, output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +150,36 @@ class ResolvedDeclarationEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
return output.join('');
|
||||
}
|
||||
|
||||
emitDeclaration(symbol: ts.Symbol, decl: ts.Node, output: string[]) {
|
||||
// The declaration node may not be a complete statement, e.g. for var/const
|
||||
// symbols. We need to find the complete export statement by traversing
|
||||
// upwards.
|
||||
while (!hasModifier(decl, ts.SyntaxKind.ExportKeyword) && decl.parent) {
|
||||
decl = decl.parent;
|
||||
}
|
||||
|
||||
if (hasModifier(decl, ts.SyntaxKind.ExportKeyword)) {
|
||||
// Make an empty line between two exports
|
||||
if (output.length) {
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
const jsdocComment = this.processJsDocTags(decl, this.options.exportTags);
|
||||
if (jsdocComment) {
|
||||
output.push(jsdocComment + '\n');
|
||||
}
|
||||
|
||||
output.push(stripEmptyLines(this.emitNode(decl)) + '\n');
|
||||
} else {
|
||||
// This may happen for symbols re-exported from external modules.
|
||||
this.diagnostics.push({
|
||||
type: 'warn',
|
||||
message: createErrorMessage(decl, `No export declaration found for symbol "${symbol.name}"`)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private isExportPatternStripped(symbolName: string): boolean {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface TypeOnly { field: string; }
|
||||
export interface TypeAndValue { field: string; }
|
||||
|
||||
export const TypeAndValue: Function;
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface TypeAndValue { field: string; }
|
||||
|
||||
export const TypeAndValue: Function;
|
||||
|
||||
export interface TypeOnly { field: string; }
|
||||
@@ -45,6 +45,12 @@ describe('integration test: public api', () => {
|
||||
'test/fixtures/classes_and_interfaces_expected.d.ts');
|
||||
});
|
||||
|
||||
it('should include value and type', () => {
|
||||
check(
|
||||
'test/fixtures/exports_type_and_value.d.ts',
|
||||
'test/fixtures/exports_type_and_value_expected.d.ts');
|
||||
});
|
||||
|
||||
it('should include members reexported classes', () => {
|
||||
check(
|
||||
'test/fixtures/reexported_classes.d.ts', 'test/fixtures/reexported_classes_expected.d.ts');
|
||||
|
||||
Reference in New Issue
Block a user