Skip to content

Commit b64a276

Browse files
mheveryIgorMinar
authored andcommitted
refactor(ivy): make return value of define(Component|Directive|Pipe|Injector|Injectable) private (#23371) (#23383)
Ivy definition looks something like this: ``` class MyService { static ngInjectableDef = defineInjectable({ … }); } ``` Here the argument to `defineInjectable` is well known public contract which needs to be honored in backward compatible way between versions. The type of the return value of `defineInjectable` on the other hand is private and can change shape drastically between versions without effecting backwards compatibility of libraries publish to NPM. To our users it is effectively an opaque token. For this reson why declare the return value of `defineInjectable` as `never`. PR Close #23383
1 parent 815ae29 commit b64a276

21 files changed

+105
-96
lines changed

packages/core/src/change_detection/differs/iterable_differs.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {InjectableDef, defineInjectable} from '../../di/defs';
9+
import {defineInjectable} from '../../di/defs';
1010
import {Optional, SkipSelf} from '../../di/metadata';
1111
import {StaticProvider} from '../../di/provider';
1212
import {DefaultIterableDifferFactory} from '../differs/default_iterable_differ';
@@ -137,7 +137,7 @@ export interface IterableDifferFactory {
137137
*
138138
*/
139139
export class IterableDiffers {
140-
static ngInjectableDef: InjectableDef<IterableDiffers> = defineInjectable({
140+
static ngInjectableDef = defineInjectable({
141141
providedIn: 'root',
142142
factory: () => new IterableDiffers([new DefaultIterableDifferFactory()])
143143
});

packages/core/src/di.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*/
1414

1515
export * from './di/metadata';
16-
export * from './di/defs';
16+
export {InjectableType, InjectorType, defineInjectable, defineInjector} from './di/defs';
1717
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
1818
export {Injectable, InjectableDecorator, InjectableProvider} from './di/injectable';
1919
export {inject, InjectFlags, INJECTOR, Injector} from './di/injector';

packages/core/src/di/defs.ts

+23-19
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ import {ClassProvider, ClassSansProvider, ConstructorProvider, ConstructorSansPr
2020
* `InjectorDef`, `NgModule`, or a special scope (e.g. `'root'`). A value of `null` indicates
2121
* that the injectable does not belong to any scope.
2222
*
23-
* This type is typically generated by the Angular compiler, but can be hand-written if needed.
24-
*
25-
* @experimental
23+
* NOTE: This is a private type and should not be exported
2624
*/
2725
export interface InjectableDef<T> {
2826
/**
@@ -54,7 +52,7 @@ export interface InjectableDef<T> {
5452
* structure of providers with a defined priority (identically to how `NgModule`s also have
5553
* an import/dependency structure).
5654
*
57-
* @experimental
55+
* NOTE: This is a private type and should not be exported
5856
*/
5957
export interface InjectorDef<T> {
6058
factory: () => T;
@@ -75,7 +73,12 @@ export interface InjectorDef<T> {
7573
*
7674
* @experimental
7775
*/
78-
export interface InjectableType<T> extends Type<T> { ngInjectableDef: InjectableDef<T>; }
76+
export interface InjectableType<T> extends Type<T> {
77+
/**
78+
* Opaque type whose structure is highly version dependent. Do not rely on any properties.
79+
*/
80+
ngInjectableDef: never;
81+
}
7982

8083
/**
8184
* A type which has an `InjectorDef` static field.
@@ -84,15 +87,20 @@ export interface InjectableType<T> extends Type<T> { ngInjectableDef: Injectable
8487
*
8588
* @experimental
8689
*/
87-
export interface InjectorType<T> extends Type<T> { ngInjectorDef: InjectorDef<T>; }
90+
export interface InjectorType<T> extends Type<T> {
91+
/**
92+
* Opaque type whose structure is highly version dependent. Do not rely on any properties.
93+
*/
94+
ngInjectorDef: never;
95+
}
8896

8997
/**
9098
* Describes the `InjectorDef` equivalent of a `ModuleWithProviders`, an `InjectorDefType` with an
9199
* associated array of providers.
92100
*
93101
* Objects of this type can be listed in the imports section of an `InjectorDef`.
94102
*
95-
* @experimental
103+
* NOTE: This is a private type and should not be exported
96104
*/
97105
export interface InjectorTypeWithProviders<T> {
98106
ngModule: InjectorType<T>;
@@ -120,12 +128,10 @@ export interface InjectorTypeWithProviders<T> {
120128
export function defineInjectable<T>(opts: {
121129
providedIn?: Type<any>| 'root' | 'any' | null,
122130
factory: () => T,
123-
}): InjectableDef<T> {
124-
return {
125-
providedIn: opts.providedIn as any || null,
126-
factory: opts.factory,
127-
value: undefined,
128-
};
131+
}): never {
132+
return ({
133+
providedIn: opts.providedIn as any || null, factory: opts.factory, value: undefined,
134+
} as InjectableDef<T>) as never;
129135
}
130136

131137
/**
@@ -149,10 +155,8 @@ export function defineInjectable<T>(opts: {
149155
* @experimental
150156
*/
151157
export function defineInjector(options: {factory: () => any, providers?: any[], imports?: any[]}):
152-
InjectorDef<any> {
153-
return {
154-
factory: options.factory,
155-
providers: options.providers || [],
156-
imports: options.imports || [],
157-
};
158+
never {
159+
return ({
160+
factory: options.factory, providers: options.providers || [], imports: options.imports || [],
161+
} as InjectorDef<any>) as never;
158162
}

packages/core/src/di/injection_token.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class InjectionToken<T> {
5252
/** @internal */
5353
readonly ngMetadataName = 'InjectionToken';
5454

55-
readonly ngInjectableDef: InjectableDef<T>|undefined;
55+
readonly ngInjectableDef: never|undefined;
5656

5757
constructor(protected _desc: string, options?: {
5858
providedIn?: Type<any>| 'root' | null,
@@ -71,6 +71,4 @@ export class InjectionToken<T> {
7171
toString(): string { return `InjectionToken ${this._desc}`; }
7272
}
7373

74-
export interface InjectableDefToken<T> extends InjectionToken<T> {
75-
ngInjectableDef: InjectableDef<T>;
76-
}
74+
export interface InjectableDefToken<T> extends InjectionToken<T> { ngInjectableDef: never; }

packages/core/src/di/r3_injector.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ export class R3Injector {
326326
}
327327

328328
function injectableDefRecord(token: Type<any>| InjectionToken<any>): Record<any> {
329-
const def = (token as InjectableType<any>).ngInjectableDef;
329+
const def = (token as InjectableType<any>).ngInjectableDef as InjectableDef<any>;
330330
if (def === undefined) {
331331
throw new Error(`Type ${stringify(token)} is missing an ngInjectableDef definition.`);
332332
}

packages/core/src/render3/definition.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ export function defineComponent<T>(componentDefinition: {
162162
* `PipeDefs`s. The function is necessary to be able to support forward declarations.
163163
*/
164164
pipes?: PipeTypesOrFactory | null;
165-
}): ComponentDef<T> {
165+
}): never {
166166
const type = componentDefinition.type;
167167
const pipeTypes = componentDefinition.pipes !;
168168
const directiveTypes = componentDefinition.directives !;
@@ -196,7 +196,7 @@ export function defineComponent<T>(componentDefinition: {
196196
};
197197
const feature = componentDefinition.features;
198198
feature && feature.forEach((fn) => fn(def));
199-
return def;
199+
return def as never;
200200
}
201201

202202
export function extractDirectiveDef(type: DirectiveType<any>& ComponentType<any>):
@@ -400,7 +400,7 @@ export const defineDirective = defineComponent as any as<T>(directiveDefinition:
400400
* See: {@link Directive.exportAs}
401401
*/
402402
exportAs?: string;
403-
}) => DirectiveDef<T>;
403+
}) => never;
404404

405405
/**
406406
* Create a pipe definition object.
@@ -428,11 +428,11 @@ export function definePipe<T>(pipeDef: {
428428

429429
/** Whether the pipe is pure. */
430430
pure?: boolean
431-
}): PipeDef<T> {
432-
return <PipeDef<T>>{
431+
}): never {
432+
return (<PipeDef<T>>{
433433
name: pipeDef.name,
434434
n: pipeDef.factory,
435435
pure: pipeDef.pure !== false,
436436
onDestroy: pipeDef.type.prototype.ngOnDestroy || null
437-
};
437+
}) as never;
438438
}

packages/core/src/render3/interfaces/definition.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,21 @@ export const enum RenderFlags {
3838
* A subclass of `Type` which has a static `ngComponentDef`:`ComponentDef` field making it
3939
* consumable for rendering.
4040
*/
41-
export interface ComponentType<T> extends Type<T> { ngComponentDef: ComponentDef<T>; }
41+
export interface ComponentType<T> extends Type<T> { ngComponentDef: never; }
4242

4343
/**
4444
* A subclass of `Type` which has a static `ngDirectiveDef`:`DirectiveDef` field making it
4545
* consumable for rendering.
4646
*/
47-
export interface DirectiveType<T> extends Type<T> { ngDirectiveDef: DirectiveDef<T>; }
47+
export interface DirectiveType<T> extends Type<T> { ngDirectiveDef: never; }
4848

4949
export const enum DirectiveDefFlags {ContentQuery = 0b10}
5050

5151
/**
5252
* A subclass of `Type` which has a static `ngPipeDef`:`PipeDef` field making it
5353
* consumable for rendering.
5454
*/
55-
export interface PipeType<T> extends Type<T> { ngPipeDef: PipeDef<T>; }
55+
export interface PipeType<T> extends Type<T> { ngPipeDef: never; }
5656

5757
/**
5858
* Runtime link information for Directives.

packages/core/test/bundling/injection/usage.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {InjectableDef, Injector, InjectorDef, createInjector, defineInjectable, defineInjector} from '@angular/core';
9+
import {Injector, createInjector, defineInjectable, defineInjector} from '@angular/core';
1010

1111
export class RootService {
1212
static ngInjectableDef = defineInjectable({

packages/core/test/render3/compiler_canonical/back_patch_types_specs.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Component, ContentChild, Directive, Injectable, Injector, InjectorDef, Input, NgModule, NgModuleFactory, NgModuleRef, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, defineInjector} from '../../../src/core';
9+
import {Component, ContentChild, Directive, Injectable, Injector, Input, NgModule, NgModuleFactory, NgModuleRef, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, defineInjector} from '../../../src/core';
1010
import * as r3 from '../../../src/render3/index';
1111

1212
const details_elided = {

packages/core/test/render3/compiler_canonical/component_directives_spec.ts

+25-14
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88

99
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
1010
import * as $r3$ from '../../../src/core_render3_private_export';
11+
import {ComponentDef} from '../../../src/render3/interfaces/definition';
1112
import {renderComponent, toHtml} from '../render_util';
1213

14+
15+
1316
/// See: `normative.md`
1417
describe('components & directives', () => {
1518
type $RenderFlags$ = $r3$.ɵRenderFlags;
@@ -76,8 +79,8 @@ describe('components & directives', () => {
7679
}
7780

7881
// NON-NORMATIVE (done by defineNgModule)
79-
MyComponent.ngComponentDef.directiveDefs =
80-
[ChildComponent.ngComponentDef, SomeDirective.ngDirectiveDef];
82+
(MyComponent.ngComponentDef as ComponentDef<any>).directiveDefs =
83+
[(ChildComponent.ngComponentDef as ComponentDef<any>), SomeDirective.ngDirectiveDef];
8184
// /NON-NORMATIVE
8285

8386
expect(renderComp(MyComponent)).toEqual('<child some-directive="">child-view</child>!');
@@ -126,7 +129,7 @@ describe('components & directives', () => {
126129
}
127130

128131
// NON-NORMATIVE (done by defineNgModule)
129-
MyApp.ngComponentDef.directiveDefs = [HostBindingDir.ngDirectiveDef];
132+
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs = [HostBindingDir.ngDirectiveDef];
130133
// /NON-NORMATIVE
131134

132135
expect(renderComp(MyApp)).toEqual(`<div hostbindingdir="" id="some id"></div>`);
@@ -177,7 +180,7 @@ describe('components & directives', () => {
177180
}
178181

179182
// NON-NORMATIVE (done by defineNgModule)
180-
MyApp.ngComponentDef.directiveDefs = [HostListenerDir.ngDirectiveDef];
183+
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs = [HostListenerDir.ngDirectiveDef];
181184
// /NON-NORMATIVE
182185

183186
expect(renderComp(MyApp)).toEqual(`<button hostlistenerdir="">Click</button>`);
@@ -222,7 +225,7 @@ describe('components & directives', () => {
222225
}
223226

224227
// NON-NORMATIVE (done by defineNgModule)
225-
MyApp.ngComponentDef.directiveDefs = [HostAttributeDir.ngDirectiveDef];
228+
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs = [HostAttributeDir.ngDirectiveDef];
226229
// /NON-NORMATIVE
227230

228231
expect(renderComp(MyApp)).toEqual(`<div hostattributedir="" role="listbox"></div>`);
@@ -270,7 +273,7 @@ describe('components & directives', () => {
270273
}
271274

272275
// NON-NORMATIVE (done by defineNgModule)
273-
MyApp.ngComponentDef.directiveDefs = [HostBindingDir.ngDirectiveDef];
276+
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs = [HostBindingDir.ngDirectiveDef];
274277
// /NON-NORMATIVE
275278

276279
expect(renderComp(MyApp)).toEqual(`<div aria-label="some label" hostbindingdir=""></div>`);
@@ -333,7 +336,8 @@ describe('components & directives', () => {
333336
}
334337

335338
// NON-NORMATIVE (done by defineNgModule)
336-
MyApp.ngComponentDef.directiveDefs = [MyComp.ngComponentDef];
339+
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
340+
[(MyComp.ngComponentDef as ComponentDef<any>)];
337341
// /NON-NORMATIVE
338342

339343
expect(renderComp(MyApp)).toEqual(`<my-comp>some name</my-comp>`);
@@ -463,7 +467,8 @@ describe('components & directives', () => {
463467
}
464468

465469
// NON-NORMATIVE (done by defineNgModule)
466-
MyApp.ngComponentDef.directiveDefs = [MyArrayComp.ngComponentDef];
470+
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
471+
[(MyArrayComp.ngComponentDef as ComponentDef<any>)];
467472
// /NON-NORMATIVE
468473

469474
expect(renderComp(MyApp)).toEqual(`<my-array-comp>Nancy Bess</my-array-comp>`);
@@ -507,7 +512,8 @@ describe('components & directives', () => {
507512
}
508513

509514
// NON-NORMATIVE (done by defineNgModule)
510-
MyApp.ngComponentDef.directiveDefs = [MyArrayComp.ngComponentDef];
515+
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
516+
[(MyArrayComp.ngComponentDef as ComponentDef<any>)];
511517
// /NON-NORMATIVE
512518

513519
expect(renderComp(MyApp)).toEqual(`<my-array-comp>NANCY Bess</my-array-comp>`);
@@ -567,7 +573,8 @@ describe('components & directives', () => {
567573
}
568574

569575
// NON-NORMATIVE (done by defineNgModule)
570-
MyApp.ngComponentDef.directiveDefs = [MyComp.ngComponentDef];
576+
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
577+
[(MyComp.ngComponentDef as ComponentDef<any>)];
571578
// /NON-NORMATIVE
572579

573580
expect(renderComp(MyApp)).toEqual(`<my-comp>3</my-comp>`);
@@ -609,7 +616,8 @@ describe('components & directives', () => {
609616
}
610617

611618
// NON-NORMATIVE (done by defineNgModule)
612-
MyApp.ngComponentDef.directiveDefs = [MyArrayComp.ngComponentDef];
619+
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
620+
[(MyArrayComp.ngComponentDef as ComponentDef<any>)];
613621
// /NON-NORMATIVE
614622

615623
expect(renderComp(MyApp)).toEqual(`<my-array-comp>Nancy Bess</my-array-comp>`);
@@ -721,7 +729,8 @@ describe('components & directives', () => {
721729
}
722730

723731
// NON-NORMATIVE (done by defineNgModule)
724-
MyApp.ngComponentDef.directiveDefs = [MyComp.ngComponentDef];
732+
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
733+
[(MyComp.ngComponentDef as ComponentDef<any>)];
725734
// /NON-NORMATIVE
726735

727736
expect(renderComp(MyApp)).toEqual(`<my-comp>start-abcde-middle-fghi-end</my-comp>`);
@@ -795,7 +804,8 @@ describe('components & directives', () => {
795804
}
796805

797806
// NON-NORMATIVE (done by defineNgModule)
798-
MyApp.ngComponentDef.directiveDefs = [ObjectComp.ngComponentDef];
807+
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
808+
[(ObjectComp.ngComponentDef as ComponentDef<any>)];
799809
// /NON-NORMATIVE
800810

801811
expect(renderComp(MyApp)).toEqual(`<object-comp><p>500</p><p>slide</p></object-comp>`);
@@ -882,7 +892,8 @@ describe('components & directives', () => {
882892
}
883893

884894
// NON-NORMATIVE (done by defineNgModule)
885-
MyApp.ngComponentDef.directiveDefs = [NestedComp.ngComponentDef];
895+
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
896+
[(NestedComp.ngComponentDef as ComponentDef<any>)];
886897
// /NON-NORMATIVE
887898

888899
expect(renderComp(MyApp))

packages/core/test/render3/compiler_canonical/elements_spec.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
*/
88

99
import {browserDetection} from '@angular/platform-browser/testing/src/browser_util';
10+
1011
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
1112
import * as $r3$ from '../../../src/core_render3_private_export';
13+
import {ComponentDef} from '../../../src/render3/interfaces/definition';
1214
import {ComponentFixture, renderComponent, toHtml} from '../render_util';
1315

16+
1417
/// See: `normative.md`
1518
describe('elements', () => {
1619
// Saving type as $any$, etc to simplify testing for compiler, as types aren't saved
@@ -103,7 +106,7 @@ describe('elements', () => {
103106
}
104107

105108
// NON-NORMATIVE
106-
LocalRefComp.ngComponentDef.directiveDefs = () => [Dir.ngDirectiveDef];
109+
(LocalRefComp.ngComponentDef as ComponentDef<any>).directiveDefs = () => [Dir.ngDirectiveDef];
107110
// /NON-NORMATIVE
108111

109112
const fixture = new ComponentFixture(LocalRefComp);

packages/core/test/render3/compiler_canonical/injection_spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, INJECTOR, Inject, InjectFlags, Injectable, InjectableDef, Injector, InjectorDef, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, SkipSelf, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, defineInjectable, defineInjector, inject} from '../../../src/core';
9+
import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, INJECTOR, Inject, InjectFlags, Injectable, Injector, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, SkipSelf, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, defineInjectable, defineInjector, inject} from '../../../src/core';
1010
import * as $r3$ from '../../../src/core_render3_private_export';
1111
import {renderComponent, toHtml} from '../render_util';
1212

0 commit comments

Comments
 (0)