Skip to content

Commit d1539a8

Browse files
committed
refactor(core): Fix DirectiveDefinition interface to allow abstract classes
Interface should permit abstract classes since directives can be abstract
1 parent 7541f30 commit d1539a8

14 files changed

Lines changed: 119 additions & 35 deletions

File tree

packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/GOLDEN_PARTIAL.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,3 +733,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE
733733
****************************************************************************************************/
734734
export {};
735735

736+
/****************************************************************************************************
737+
* PARTIAL FILE: abstract_directive.js
738+
****************************************************************************************************/
739+
import { Directive } from '@angular/core';
740+
import * as i0 from "@angular/core";
741+
export class AbstractDir {
742+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AbstractDir, deps: [], target: i0.ɵɵFactoryTarget.Directive });
743+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: AbstractDir, isStandalone: true, selector: "[test-dir]", ngImport: i0 });
744+
}
745+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AbstractDir, decorators: [{
746+
type: Directive,
747+
args: [{
748+
selector: '[test-dir]',
749+
}]
750+
}] });
751+
752+
/****************************************************************************************************
753+
* PARTIAL FILE: abstract_directive.d.ts
754+
****************************************************************************************************/
755+
import * as i0 from "@angular/core";
756+
export declare abstract class AbstractDir {
757+
static ɵfac: i0.ɵɵFactoryDeclaration<AbstractDir, never>;
758+
static ɵdir: i0.ɵɵDirectiveDeclaration<AbstractDir, "[test-dir]", never, {}, {}, never, never, true, never>;
759+
}
760+

packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/TEST_CASES.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,22 @@
163163
"files": ["nested_component_definition.js"]
164164
}
165165
]
166+
},
167+
{
168+
"description": "should support abstract directives",
169+
"inputFiles": ["abstract_directive.ts"],
170+
"expectations": [
171+
{
172+
"failureMessage": "Incorrect generated output",
173+
"files": [
174+
{
175+
"expected": "abstract_directive.js",
176+
"generated": "abstract_directive.js"
177+
}
178+
]
179+
}
180+
],
181+
"compilationModeFilter": ["full compile", "instruction compile"]
166182
}
167183
]
168184
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Directive } from '@angular/core';
2+
import * as i0 from "@angular/core";
3+
4+
5+
export class AbstractDir {
6+
7+
static ɵfac = function AbstractDir_Factory(__ngFactoryType__) {
8+
9+
return new (__ngFactoryType__ || AbstractDir)();
10+
};
11+
static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({
12+
type: AbstractDir,
13+
selectors: [["", "test-dir", ""]]
14+
});
15+
}
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {Directive} from '@angular/core';
2+
3+
@Directive({
4+
selector: '[test-dir]',
5+
})
6+
export abstract class AbstractDir {
7+
}

packages/core/src/di/provider_collection.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {RuntimeError, RuntimeErrorCode} from '../errors';
10-
import {Type} from '../interface/type';
10+
import {AbstractType, Type} from '../interface/type';
1111
import {getComponentDef} from '../render3/def_getters';
1212
import {getFactoryDef} from '../render3/definition_factory';
1313
import {cyclicDependencyErrorWithDetails, throwInvalidProviderError} from '../render3/errors_di';
@@ -100,7 +100,7 @@ export type ImportProvidersSource =
100100

101101
type WalkProviderTreeVisitor = (
102102
provider: SingleProvider,
103-
container: Type<unknown> | InjectorType<unknown>,
103+
container: Type<unknown> | AbstractType<unknown> | InjectorType<unknown>,
104104
) => void;
105105

106106
/**
@@ -150,10 +150,10 @@ export function importProvidersFrom(...sources: ImportProvidersSource[]): Enviro
150150

151151
export function internalImportProvidersFrom(
152152
checkForStandaloneCmp: boolean,
153-
...sources: ImportProvidersSource[]
153+
...sources: (ImportProvidersSource | AbstractType<unknown>)[]
154154
): Provider[] {
155155
const providersOut: SingleProvider[] = [];
156-
const dedup = new Set<Type<unknown>>(); // already seen types
156+
const dedup = new Set<Type<unknown> | AbstractType<unknown>>(); // already seen types
157157
let injectorTypesWithProviders: InjectorTypeWithProviders<unknown>[] | undefined;
158158

159159
const collectProviders: WalkProviderTreeVisitor = (provider) => {
@@ -230,10 +230,10 @@ export type SingleProvider =
230230
* an injector definition are processed. (following View Engine semantics: see FW-1349)
231231
*/
232232
export function walkProviderTree(
233-
container: Type<unknown> | InjectorTypeWithProviders<unknown>,
233+
container: Type<unknown> | AbstractType<unknown> | InjectorTypeWithProviders<unknown>,
234234
visitor: WalkProviderTreeVisitor,
235-
parents: Type<unknown>[],
236-
dedup: Set<Type<unknown>>,
235+
parents: (Type<unknown> | AbstractType<unknown>)[],
236+
dedup: Set<Type<unknown> | AbstractType<unknown>>,
237237
): container is InjectorTypeWithProviders<unknown> {
238238
container = resolveForwardRef(container);
239239
if (!container) return false;

packages/core/src/render3/debug/framework_injector_profiler.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import {Injector} from '../../di/injector';
1010
import {EnvironmentInjector} from '../../di/r3_injector';
11-
import {Type} from '../../interface/type';
11+
import {AbstractType, Type} from '../../interface/type';
1212
import {assertDefined, throwError} from '../../util/assert';
1313
import {assertTNodeForLView} from '../assert';
1414
import {getComponentDef} from '../def_getters';
@@ -67,7 +67,7 @@ import {
6767
class DIDebugData {
6868
resolverToTokenToDependencies = new WeakMap<
6969
Injector | LView,
70-
WeakMap<Type<unknown>, InjectedService[]>
70+
WeakMap<Type<unknown> | AbstractType<unknown>, InjectedService[]>
7171
>();
7272
resolverToProviders = new WeakMap<Injector | TNode, ProviderRecord[]>();
7373
resolverToEffects = new WeakMap<
@@ -79,7 +79,7 @@ class DIDebugData {
7979
reset() {
8080
this.resolverToTokenToDependencies = new WeakMap<
8181
Injector | LView,
82-
WeakMap<Type<unknown>, InjectedService[]>
82+
WeakMap<Type<unknown> | AbstractType<unknown>, InjectedService[]>
8383
>();
8484
this.resolverToProviders = new WeakMap<Injector | TNode, ProviderRecord[]>();
8585
this.standaloneInjectorToComponent = new WeakMap<Injector, Type<unknown>>();

packages/core/src/render3/debug/injector_profiler.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {InjectionToken} from '../../di/injection_token';
1212
import type {Injector} from '../../di/injector';
1313
import {InjectOptions, InternalInjectFlags} from '../../di/interface/injector';
1414
import type {SingleProvider} from '../../di/provider_collection';
15-
import {Type} from '../../interface/type';
15+
import {AbstractType, Type} from '../../interface/type';
1616
import {throwError} from '../../util/assert';
1717
import type {TNode} from '../interfaces/node';
1818
import type {LView} from '../interfaces/view';
@@ -70,7 +70,7 @@ export interface InjectorProfilerContext {
7070
* - Example: if ModuleA --provides--> ServiceA --injects--> ServiceB
7171
* then inject(ServiceB) in ServiceA has ServiceA as a construction context
7272
*/
73-
token: Type<unknown> | null;
73+
token: Type<unknown> | AbstractType<unknown> | null;
7474
}
7575

7676
export interface InjectedServiceEvent {
@@ -145,7 +145,7 @@ export interface ProviderRecord {
145145
/**
146146
* The path of DI containers that were followed to import this provider
147147
*/
148-
importPath?: Type<unknown>[];
148+
importPath?: (Type<unknown> | AbstractType<unknown>)[];
149149
}
150150

151151
/**

packages/core/src/render3/definition.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import {ChangeDetectionStrategy} from '../change_detection/constants';
1010
import {EnvironmentInjector} from '../di/r3_injector';
1111
import {formatRuntimeError, RuntimeErrorCode} from '../errors';
12-
import {Type, Writable} from '../interface/type';
12+
import {AbstractType, Type, Writable} from '../interface/type';
1313
import {NgModuleDef} from '../metadata/ng_module_def';
1414
import {SchemaMetadata} from '../metadata/schema';
1515
import {ViewEncapsulation} from '../metadata/view';
@@ -111,7 +111,7 @@ interface DirectiveDefinition<T> {
111111
/**
112112
* Directive type, needed to configure the injector.
113113
*/
114-
type: Type<T>;
114+
type: Type<T> | AbstractType<T>;
115115

116116
/** The selectors that will be used to match nodes to this directive. */
117117
selectors?: (string | number)[][];
@@ -215,6 +215,9 @@ interface DirectiveDefinition<T> {
215215
}
216216

217217
interface ComponentDefinition<T> extends Omit<DirectiveDefinition<T>, 'features'> {
218+
/** Component type, needed to configure the injector. */
219+
type: Type<T>;
220+
218221
/**
219222
* The number of nodes, local refs, and pipes in this component template.
220223
*
@@ -344,6 +347,7 @@ export function ɵɵdefineComponent<T>(
344347
const baseDef = getNgDirectiveDef(componentDefinition as DirectiveDefinition<T>);
345348
const def: Writable<ComponentDef<T>> = {
346349
...baseDef,
350+
type: componentDefinition.type,
347351
decls: componentDefinition.decls,
348352
vars: componentDefinition.vars,
349353
template: componentDefinition.template,

packages/core/src/render3/features/inherit_definition_feature.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {RuntimeError, RuntimeErrorCode} from '../../errors';
10-
import {Type, Writable} from '../../interface/type';
10+
import {AbstractType, Type, Writable} from '../../interface/type';
1111
import {EMPTY_ARRAY, EMPTY_OBJ} from '../../util/empty';
1212
import {fillProperties} from '../../util/property';
1313
import {NG_COMP_DEF, NG_DIR_DEF} from '../fields';
@@ -26,8 +26,8 @@ import {mergeHostAttrs} from '../util/attrs_utils';
2626
import {stringifyForError} from '../util/stringify_utils';
2727

2828
export function getSuperType(
29-
type: Type<any>,
30-
): Type<any> & {ɵcmp?: ComponentDef<any>; ɵdir?: DirectiveDef<any>} {
29+
type: Type<any> | AbstractType<any>,
30+
): (Type<any> | AbstractType<any>) & {ɵcmp?: ComponentDef<any>; ɵdir?: DirectiveDef<any>} {
3131
return Object.getPrototypeOf(type.prototype).constructor;
3232
}
3333

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import {InputSignalNode} from '../../authoring/input/input_signal_node';
1010
import {ModuleWithProviders, ProcessProvidersFunction} from '../../di/interface/provider';
1111
import {EnvironmentInjector} from '../../di/r3_injector';
12-
import {Type} from '../../interface/type';
12+
import {AbstractType, Type} from '../../interface/type';
1313
import {SchemaMetadata} from '../../metadata/schema';
1414
import {ViewEncapsulation} from '../../metadata/view';
1515
import {FactoryFn} from '../definition_factory';
@@ -200,7 +200,7 @@ export interface DirectiveDef<T> {
200200
readonly hostAttrs: TAttributes | null;
201201

202202
/** Token representing the directive. Used by DI. */
203-
readonly type: Type<T>;
203+
readonly type: Type<T> | AbstractType<T>;
204204

205205
/** Function that resolves `providers` and publishes them into the DI system. */
206206
providersResolver: ProvidersResolver | null;
@@ -295,6 +295,9 @@ export interface DirectiveDef<T> {
295295
* See: {@link defineComponent}
296296
*/
297297
export interface ComponentDef<T> extends DirectiveDef<T> {
298+
/** Token representing the component. Used by DI. */
299+
readonly type: Type<T>;
300+
298301
/**
299302
* Unique ID for the component. Used in view encapsulation and
300303
* to keep track of the injector in standalone components.
@@ -432,7 +435,7 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
432435
*/
433436
export interface PipeDef<T> {
434437
/** Token representing the pipe. */
435-
type: Type<T>;
438+
type: Type<T> | AbstractType<T>;
436439

437440
/**
438441
* Pipe name.

0 commit comments

Comments
 (0)