Skip to content
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class TokenModule {

export const TOKEN = new InjectionToken('test', {
providedIn: TokenModule,
factory: () => new Service(inject(Dep)),
factory: () => new Service(inject(Dep), inject(AbstractDep)),
});


Expand Down Expand Up @@ -49,6 +49,11 @@ export class Dep {
readonly data = 'fromToken';
}

@Injectable({providedIn: 'root', useValue: {value: 42}})
export abstract class AbstractDep {
abstract value: number;
}

export class Service {
constructor(readonly dep: Dep) {}
constructor(readonly dep: Dep, unused: AbstractDep) {}
}
9 changes: 8 additions & 1 deletion packages/compiler/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,14 @@ export const createSelf = makeMetadataFactory('Self');
export const createSkipSelf = makeMetadataFactory('SkipSelf');
export const createHost = makeMetadataFactory('Host');

export interface Type extends Function { new (...args: any[]): any; }
export interface AbstractType extends Function { prototype: any; }
export interface ConcreteType extends Function { new (...args: any[]): any; }

// See https://github.com/angular/angular/pull/25222#issuecomment-426423317 for why
// we have to use union rather than extends. Using extends is a breaking change because
// we get into situations where we both narrow and restrict types during casting which
// causes TypeScript to fail.
export type Type = AbstractType | ConcreteType;
export const Type = Function;

export enum SecurityContext {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/application_ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ export class ApplicationRef {

// Create a factory associated with the current module if it's not bound to some other
const ngModule = componentFactory instanceof ComponentFactoryBoundToModule ?
null :
undefined :

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reasoning to change null to undefined?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

abstract create(
injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any,
ngModule?: NgModuleRef<any>): ComponentRef<C>;

ComponentFactory#create() accepts NgModuleRef | undefined, not NgModuleRef | null, it's a compilation error in TypeScript.

Previously ngModule is of any type so bypassed the type-checking.

this._injector.get(NgModuleRef);
const selectorOrNode = rootSelectorOrNode || componentFactory.selector;
const compRef = componentFactory.create(Injector.NULL, [], selectorOrNode, ngModule);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export * from './platform_core_providers';
export {TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID, MissingTranslationStrategy} from './i18n/tokens';
export {ApplicationModule} from './application_module';
export {wtfCreateScope, wtfLeave, wtfStartTimeRange, wtfEndTimeRange, WtfScopeFn} from './profile/profile';
export {Type} from './type';
export {Type, AbstractType, ConcreteType} from './type';
export {EventEmitter} from './event_emitter';
export {ErrorHandler} from './error_handler';
export * from './core_private_export';
Expand Down
20 changes: 12 additions & 8 deletions packages/core/src/di/defs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
*/

import {NG_INJECTABLE_DEF, NG_INJECTOR_DEF} from '../render3/fields';
import {Type} from '../type';
import {ConcreteType, Type} from '../type';

import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, ValueProvider} from './provider';


import {ClassProvider, ClassSansProvider, ConstructorProvider, ConstructorSansProvider, ExistingProvider, ExistingSansProvider, FactoryProvider, FactorySansProvider, StaticClassProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './provider';

/**
* Information about how a type or `InjectionToken` interfaces with the DI system.
Expand Down Expand Up @@ -60,8 +62,8 @@ export interface InjectorDef<T> {

// TODO(alxhub): Narrow down the type here once decorators properly change the return type of the
// class they are decorating (to add the ngInjectableDef property for example).
providers: (Type<any>|ValueProvider|ExistingProvider|FactoryProvider|ConstructorProvider|
StaticClassProvider|ClassProvider|any[])[];
providers: (InjectableType<any>|ValueProvider|ExistingProvider|FactoryProvider|
ConstructorProvider|StaticClassProvider|ClassProvider|any[])[];

imports: (InjectorType<any>|InjectorTypeWithProviders<any>)[];
}
Expand All @@ -74,26 +76,28 @@ export interface InjectorDef<T> {
*
* @experimental
*/
export interface InjectableType<T> extends Type<T> {
export type InjectableType<T> = Type<T>& {
/**
* Opaque type whose structure is highly version dependent. Do not rely on any properties.
*/
ngInjectableDef: never;
}
};

/**
* A type which has an `InjectorDef` static field.
*
* `InjectorDefTypes` can be used to configure a `StaticInjector`.
*
* NOTE: This must be `ConcreteType` because the `InjectorType` itself will be eagerly instantiated.
*
* @experimental
*/
export interface InjectorType<T> extends Type<T> {
export type InjectorType<T> = ConcreteType<T>& {
/**
* Opaque type whose structure is highly version dependent. Do not rely on any properties.
*/
ngInjectorDef: never;
}
};

/**
* Describes the `InjectorDef` equivalent of a `ModuleWithProviders`, an `InjectorDefType` with an
Expand Down
14 changes: 8 additions & 6 deletions packages/core/src/di/forward_ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Type} from '../type';
import {stringify} from '../util';
import {getClosureSafeProperty} from '../util/property';



Expand All @@ -20,7 +20,7 @@ import {stringify} from '../util';
* {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref_fn'}
* @experimental
*/
export interface ForwardRefFn { (): any; }
export interface ForwardRefFn<T> { (): T; }

/**
* Allows to refer to references which are not yet defined.
Expand All @@ -34,10 +34,10 @@ export interface ForwardRefFn { (): any; }
* {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref'}
* @experimental
*/
export function forwardRef(forwardRefFn: ForwardRefFn): Type<any> {
export function forwardRef<T>(forwardRefFn: ForwardRefFn<T>): T {
(<any>forwardRefFn).__forward_ref__ = forwardRef;
(<any>forwardRefFn).toString = function() { return stringify(this()); };
return (<Type<any>><any>forwardRefFn);
return forwardRefFn as any;
}

/**
Expand All @@ -54,10 +54,12 @@ export function forwardRef(forwardRefFn: ForwardRefFn): Type<any> {
* @experimental
*/
export function resolveForwardRef(type: any): any {
if (typeof type === 'function' && type.hasOwnProperty('__forward_ref__') &&
if (typeof type === 'function' && type.hasOwnProperty(__forward_ref__) &&
type.__forward_ref__ === forwardRef) {
return (<ForwardRefFn>type)();
return (<ForwardRefFn<any>>type)();
} else {
return type;
}
}

const __forward_ref__ = getClosureSafeProperty({__forward_ref__: getClosureSafeProperty});
4 changes: 2 additions & 2 deletions packages/core/src/di/injectable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {R3_COMPILE_INJECTABLE} from '../ivy_switch/compiler/index';
import {Type} from '../type';
import {makeDecorator} from '../util/decorators';

import {InjectableDef, InjectableType} from './defs';
import {InjectableDef, InjectableType, InjectorType} from './defs';
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './provider';

/**
Expand Down Expand Up @@ -68,4 +68,4 @@ export const Injectable: InjectableDecorator = makeDecorator(
*
* @experimental
*/
export interface InjectableType<T> extends Type<T> { ngInjectableDef: InjectableDef<T>; }
export type InjectableType<T> = Type<T>& {ngInjectableDef: InjectableDef<T>};
9 changes: 5 additions & 4 deletions packages/core/src/di/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Type} from '../type';
import {ConcreteType, Type} from '../type';


/**
* Configures the `Injector` to return a value for a token.
Expand Down Expand Up @@ -71,7 +72,7 @@ export interface StaticClassSansProvider {
* An optional class to instantiate for the `token`. (If not provided `provide` is assumed to be a
* class to instantiate)
*/
useClass: Type<any>;
useClass: ConcreteType<any>;

/**
* A list of `token`s which need to be resolved by the injector. The list of values is then
Expand Down Expand Up @@ -288,7 +289,7 @@ export type StaticProvider = ValueProvider | ExistingProvider | StaticClassProvi
*
* {@example core/di/ts/provider_spec.ts region='TypeProvider'}
*/
export interface TypeProvider extends Type<any> {}
export type TypeProvider = Type<any>;

/**
* Configures the `Injector` to return a value by invoking a `useClass` function.
Expand All @@ -306,7 +307,7 @@ export interface ClassSansProvider {
/**
* Class to instantiate for the `token`.
*/
useClass: Type<any>;
useClass: ConcreteType<any>;
}

/**
Expand Down
11 changes: 6 additions & 5 deletions packages/core/src/di/r3_injector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {OnDestroy} from '../metadata/lifecycle_hooks';
import {Type} from '../type';
import {ConcreteType, Type} from '../type';
import {stringify} from '../util';

import {InjectableDef, InjectableType, InjectorType, InjectorTypeWithProviders, getInjectableDef, getInjectorDef} from './defs';
Expand All @@ -22,8 +22,8 @@ import {APP_ROOT} from './scope';
/**
* Internal type for a single provider in a deep provider array.
*/
type SingleProvider = TypeProvider | ValueProvider | ClassProvider | ConstructorProvider |
ExistingProvider | FactoryProvider | StaticClassProvider;
type SingleProvider = InjectableType<any>| TypeProvider | ValueProvider | ClassProvider |
ConstructorProvider | ExistingProvider | FactoryProvider | StaticClassProvider;

/**
* Marker which indicates that a value has not yet been created from the factory function.
Expand Down Expand Up @@ -336,7 +336,7 @@ function injectableDefRecord(token: Type<any>| InjectionToken<any>): Record<any>
}
// TODO(alxhub): there should probably be a strict mode which throws here instead of assuming a
// no-args constructor.
return makeRecord(() => new (token as Type<any>)());
return makeRecord(() => new (token as ConcreteType<any>)());
}
return makeRecord(injectableDef.factory);
}
Expand All @@ -356,7 +356,8 @@ function providerToRecord(provider: SingleProvider): Record<any> {
} else if (isFactoryProvider(provider)) {
factory = () => provider.useFactory(...injectArgs(provider.deps || []));
} else {
const classRef = (provider as StaticClassProvider | ClassProvider).useClass || token;
const classRef: ConcreteType<any> =
(provider as StaticClassProvider | ClassProvider).useClass || token;
if (hasDeps(provider)) {
factory = () => new (classRef)(...injectArgs(provider.deps));
} else {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/di/reflective_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import {Type} from '../type';
import {resolveForwardRef} from './forward_ref';
import {InjectionToken} from './injection_token';
import {Inject, Optional, Self, SkipSelf} from './metadata';
import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './provider';
import {ClassProvider, ExistingProvider, FactoryProvider, Provider, ValueProvider} from './provider';
import {invalidProviderError, mixingMultiProvidersWithRegularProvidersError, noAnnotationError} from './reflective_errors';
import {ReflectiveKey} from './reflective_key';


interface NormalizedProvider extends TypeProvider, ValueProvider, ClassProvider, ExistingProvider,
interface NormalizedProvider extends ValueProvider, ClassProvider, ExistingProvider,
FactoryProvider {}

/**
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/ivy_switch/compiler/legacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {inject, injectArgs} from '../../di/injector';
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from '../../di/provider';
import {NgModule} from '../../metadata';
import {ReflectionCapabilities} from '../../reflection/reflection_capabilities';
import {Type} from '../../type';
import {ConcreteType, Type} from '../../type';
import {getClosureSafeProperty} from '../../util/property';

import * as ivyOn from './ivy_switch_on';
Expand Down Expand Up @@ -81,7 +81,7 @@ function convertInjectableProviderToFactory(type: Type<any>, provider?: Injectab
const reflectionCapabilities = new ReflectionCapabilities();
const deps = reflectionCapabilities.parameters(type);
// TODO - convert to flags.
return () => new type(...injectArgs(deps as any[]));
return () => new (type as ConcreteType<any>)(...injectArgs(deps as any[]));
}

if (USE_VALUE in provider) {
Expand All @@ -107,7 +107,7 @@ function convertInjectableProviderToFactory(type: Type<any>, provider?: Injectab
const reflectionCapabilities = new ReflectionCapabilities();
deps = reflectionCapabilities.parameters(type);
}
return () => new type(...injectArgs(deps !));
return () => new (type as ConcreteType<any>)(...injectArgs(deps !));
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/reflection/reflection_capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Type, isType} from '../type';
import {ConcreteType, Type, isType} from '../type';
import {global, stringify} from '../util';
import {ANNOTATIONS, PARAMETERS, PROP_METADATA} from '../util/decorators';

Expand All @@ -29,7 +29,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {

isReflectionEnabled(): boolean { return true; }

factory<T>(t: Type<T>): (args: any[]) => T { return (...args: any[]) => new t(...args); }
factory<T>(t: ConcreteType<T>): (args: any[]) => T { return (...args: any[]) => new t(...args); }

/** @internal */
_zipTypesAndAnnotations(paramTypes: any[], paramAnnotations: any[]): any[][] {
Expand Down
16 changes: 8 additions & 8 deletions packages/core/src/render3/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,13 +296,13 @@ export function defineComponent<T>(componentDefinition: {
declaredInputs: declaredInputs,
outputs: invertObject(componentDefinition.outputs),
exportAs: componentDefinition.exportAs || null,
onInit: type.prototype.ngOnInit || null,
doCheck: type.prototype.ngDoCheck || null,
afterContentInit: type.prototype.ngAfterContentInit || null,
afterContentChecked: type.prototype.ngAfterContentChecked || null,
afterViewInit: type.prototype.ngAfterViewInit || null,
afterViewChecked: type.prototype.ngAfterViewChecked || null,
onDestroy: type.prototype.ngOnDestroy || null,
onInit: (type.prototype as any).ngOnInit || null,
doCheck: (type.prototype as any).ngDoCheck || null,
afterContentInit: (type.prototype as any).ngAfterContentInit || null,
afterContentChecked: (type.prototype as any).ngAfterContentChecked || null,
afterViewInit: (type.prototype as any).ngAfterViewInit || null,
afterViewChecked: (type.prototype as any).ngAfterViewChecked || null,
onDestroy: (type.prototype as any).ngOnDestroy || null,
onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush,
directiveDefs: directiveTypes ?
() => (typeof directiveTypes === 'function' ? directiveTypes() : directiveTypes)
Expand Down Expand Up @@ -660,7 +660,7 @@ export function definePipe<T>(pipeDef: {
name: pipeDef.name,
factory: pipeDef.factory,
pure: pipeDef.pure !== false,
onDestroy: pipeDef.type.prototype.ngOnDestroy || null
onDestroy: (pipeDef.type.prototype as any).ngOnDestroy || null
}) as never;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/render3/di.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {getInjectableDef, getInjectorDef} from '../di/defs';
import {InjectionToken} from '../di/injection_token';
import {InjectFlags, Injector, NullInjector, inject, setCurrentInjector} from '../di/injector';
import {Renderer2} from '../render';
import {Type} from '../type';
import {ConcreteType, Type} from '../type';

import {assertDefined} from './assert';
import {getComponentDef, getDirectiveDef, getPipeDef} from './definition';
Expand Down Expand Up @@ -495,6 +495,6 @@ export function getInheritedFactory<T>(type: Type<any>): (type: Type<T>) => T {
// (no Angular decorator on the superclass) or there is no constructor at all
// in the inheritance chain. Since the two cases cannot be distinguished, the
// latter has to be assumed.
return (t) => new t();
return (t) => new (t as ConcreteType<any>)();
}
}
12 changes: 9 additions & 3 deletions packages/core/src/render3/interfaces/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,27 @@ export const enum RenderFlags {
* A subclass of `Type` which has a static `ngComponentDef`:`ComponentDef` field making it
* consumable for rendering.
*/
export interface ComponentType<T> extends Type<T> { ngComponentDef: never; }
export type ComponentType<T> = Type<T>& {
ngComponentDef: never;
};

/**
* A subclass of `Type` which has a static `ngDirectiveDef`:`DirectiveDef` field making it
* consumable for rendering.
*/
export interface DirectiveType<T> extends Type<T> { ngDirectiveDef: never; }
export type DirectiveType<T> = Type<T>& {
ngDirectiveDef: never;
};

export const enum DirectiveDefFlags {ContentQuery = 0b10}

/**
* A subclass of `Type` which has a static `ngPipeDef`:`PipeDef` field making it
* consumable for rendering.
*/
export interface PipeType<T> extends Type<T> { ngPipeDef: never; }
export type PipeType<T> = Type<T>& {
ngPipeDef: never;
};

/**
* A version of {@link DirectiveDef} that represents the runtime type shape only, and excludes
Expand Down
Loading