refactor(core): Return NOT_FOUND for retrieve instead of null (#60358)

This fits within the spec of the retrieve.

PR Close #60358
This commit is contained in:
iteriani
2025-03-18 16:15:02 -07:00
committed by Jessica Janiuk
parent 640727434c
commit 4a107ed7af
16 changed files with 77 additions and 20 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ export class NotFoundError extends Error {
* Type guard for checking if an unknown value is a NotFound.
*/
export function isNotFound(e: unknown): e is NotFound {
return e === NOT_FOUND || (e as NotFoundError).name === 'ɵNotFound';
return e === NOT_FOUND || (e as NotFoundError)?.name === 'ɵNotFound';
}
/**
+22 -10
View File
@@ -21,6 +21,7 @@ import {ProviderToken} from './provider_token';
import type {HostAttributeToken} from './host_attribute_token';
import {
Injector as PrimitivesInjector,
isNotFound,
NotFound,
InjectionToken as PrimitivesInjectionToken,
getCurrentInjector,
@@ -50,12 +51,19 @@ export class RetrievingInjector implements PrimitivesInjector {
retrieve<T>(token: PrimitivesInjectionToken<T>, options: unknown): T | NotFound {
const flags: InternalInjectFlags =
convertToBitFlags(options as InjectOptions | undefined) || InternalInjectFlags.Default;
return (this.injector as BackwardsCompatibleInjector).get(
token as unknown as InjectionToken<T>,
// When a dependency is requested with an optional flag, DI returns null as the default value.
flags & InternalInjectFlags.Optional ? null : undefined,
flags,
) as T;
try {
return (this.injector as BackwardsCompatibleInjector).get(
token as unknown as InjectionToken<T>,
// When a dependency is requested with an optional flag, DI returns null as the default value.
(flags & InternalInjectFlags.Optional ? null : THROW_IF_NOT_FOUND) as T,
flags,
) as T;
} catch (e: any) {
if (isNotFound(e)) {
return e;
}
throw e;
}
}
}
@@ -96,11 +104,15 @@ export function injectInjectorOnly<T>(
} else if (currentInjector === null) {
return injectRootLimpMode(token, undefined, flags);
} else {
const value = currentInjector.retrieve(
token as PrimitivesInjectionToken<T>,
convertToInjectOptions(flags),
) as T;
const options = convertToInjectOptions(flags);
const value = currentInjector.retrieve(token as PrimitivesInjectionToken<T>, options) as T;
ngDevMode && emitInjectEvent(token as Type<unknown>, value, flags);
if (isNotFound(value)) {
if (options.optional) {
return null;
}
throw value;
}
return value;
}
}
+2 -2
View File
@@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {NotFoundError} from '@angular/core/primitives/di';
import {stringify} from '../util/stringify';
import type {Injector} from './injector';
import {THROW_IF_NOT_FOUND} from './injector_compatibility';
@@ -13,8 +14,7 @@ import {THROW_IF_NOT_FOUND} from './injector_compatibility';
export class NullInjector implements Injector {
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
if (notFoundValue === THROW_IF_NOT_FOUND) {
const error = new Error(`NullInjectorError: No provider for ${stringify(token)}!`);
error.name = 'NullInjectorError';
const error = new NotFoundError(`NullInjectorError: No provider for ${stringify(token)}!`);
throw error;
}
return notFoundValue;
+16 -7
View File
@@ -80,6 +80,7 @@ import {
InjectionToken as PrimitivesInjectionToken,
NOT_FOUND,
NotFound,
isNotFound,
} from '@angular/core/primitives/di';
/**
@@ -238,12 +239,19 @@ export class R3Injector extends EnvironmentInjector implements PrimitivesInjecto
retrieve<T>(token: PrimitivesInjectionToken<T>, options?: unknown): T | NotFound {
const flags: InternalInjectFlags =
convertToBitFlags(options as InjectOptions | undefined) || InternalInjectFlags.Default;
return (this as BackwardsCompatibleInjector).get(
token as unknown as InjectionToken<T>,
// When a dependency is requested with an optional flag, DI returns null as the default value.
flags & InternalInjectFlags.Optional ? null : undefined,
flags,
)!;
try {
return (this as BackwardsCompatibleInjector).get(
token as unknown as InjectionToken<T>,
// When a dependency is requested with an optional flag, DI returns null as the default value.
THROW_IF_NOT_FOUND as T,
flags,
);
} catch (e: any) {
if (isNotFound(e)) {
return e;
}
throw e;
}
}
/**
@@ -367,7 +375,8 @@ export class R3Injector extends EnvironmentInjector implements PrimitivesInjecto
: notFoundValue;
return nextInjector.get(token, notFoundValue);
} catch (e: any) {
if (e.name === 'NullInjectorError') {
if (isNotFound(e)) {
// @ts-ignore
const path: any[] = (e[NG_TEMP_TOKEN_PATH] = e[NG_TEMP_TOKEN_PATH] || []);
path.unshift(stringify(token));
if (previousInjector) {
@@ -106,6 +106,7 @@
"NG_INJ_DEF",
"NG_PIPE_DEF",
"NG_PROV_DEF",
"NOT_FOUND",
"NOT_FOUND2",
"NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR",
"NOT_YET",
@@ -123,6 +124,7 @@
"NoopAnimationDriver",
"NoopAnimationPlayer",
"NoopNgZone",
"NotFoundError",
"NullInjector",
"ObjectUnsubscribedError",
"Observable",
@@ -376,6 +378,7 @@
"isLView",
"isNodeMatchingSelector",
"isNodeMatchingSelectorList",
"isNotFound",
"isPlatformServer",
"isPositive",
"isPromise",
@@ -113,6 +113,7 @@
"NG_MOD_DEF",
"NG_PIPE_DEF",
"NG_PROV_DEF",
"NOT_FOUND",
"NOT_FOUND2",
"NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR",
"NOT_YET",
@@ -133,6 +134,7 @@
"NoopAnimationDriver",
"NoopAnimationPlayer",
"NoopNgZone",
"NotFoundError",
"NullInjector",
"ObjectUnsubscribedError",
"Observable",
@@ -399,6 +401,7 @@
"isLView",
"isNodeMatchingSelector",
"isNodeMatchingSelectorList",
"isNotFound",
"isPlatformServer",
"isPositive",
"isPromise",
@@ -82,6 +82,7 @@
"NG_MOD_DEF",
"NG_PIPE_DEF",
"NG_PROV_DEF",
"NOT_FOUND",
"NOT_FOUND2",
"NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR",
"NOT_YET",
@@ -98,6 +99,7 @@
"NodeInjectorFactory",
"NoneEncapsulationDomRenderer",
"NoopNgZone",
"NotFoundError",
"NullInjector",
"ObjectUnsubscribedError",
"Observable",
@@ -321,6 +323,7 @@
"isLView",
"isNodeMatchingSelector",
"isNodeMatchingSelectorList",
"isNotFound",
"isPlatformServer",
"isPositive",
"isPromise",
@@ -111,6 +111,7 @@
"NG_TEMPLATE_SELECTOR",
"NG_TEMP_TOKEN_PATH",
"NG_TOKEN_PATH",
"NOT_FOUND",
"NOT_FOUND2",
"NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR",
"NOT_YET",
@@ -126,6 +127,7 @@
"NodeInjectorFactory",
"NoneEncapsulationDomRenderer",
"NoopNgZone",
"NotFoundError",
"NullInjector",
"ON_COMPLETE_FNS",
"ObjectUnsubscribedError",
@@ -385,6 +387,7 @@
"isLView",
"isNodeMatchingSelector",
"isNodeMatchingSelectorList",
"isNotFound",
"isPlatformServer",
"isPositive",
"isPromise",
@@ -118,6 +118,7 @@
"NG_PROV_DEF",
"NG_VALIDATORS",
"NG_VALUE_ACCESSOR",
"NOT_FOUND",
"NOT_FOUND2",
"NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR",
"NOT_YET",
@@ -140,6 +141,7 @@
"NodeInjectorFactory",
"NoneEncapsulationDomRenderer",
"NoopNgZone",
"NotFoundError",
"NullInjector",
"ObjectUnsubscribedError",
"Observable",
@@ -470,6 +472,7 @@
"isListLikeIterable",
"isNodeMatchingSelector",
"isNodeMatchingSelectorList",
"isNotFound",
"isOptionsObj",
"isPlatformServer",
"isPositive",
@@ -110,6 +110,7 @@
"NG_PROV_DEF",
"NG_VALIDATORS",
"NG_VALUE_ACCESSOR",
"NOT_FOUND",
"NOT_FOUND2",
"NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR",
"NOT_YET",
@@ -135,6 +136,7 @@
"NodeInjectorFactory",
"NoneEncapsulationDomRenderer",
"NoopNgZone",
"NotFoundError",
"NullInjector",
"ObjectUnsubscribedError",
"Observable",
@@ -456,6 +458,7 @@
"isListLikeIterable",
"isNodeMatchingSelector",
"isNodeMatchingSelectorList",
"isNotFound",
"isOptionsObj",
"isPlatformServer",
"isPositive",
@@ -57,6 +57,7 @@
"NG_INJ_DEF",
"NG_MOD_DEF",
"NG_PROV_DEF",
"NOT_FOUND",
"NOT_FOUND2",
"NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR",
"NOT_YET",
@@ -72,6 +73,7 @@
"NodeInjectorDestroyRef",
"NodeInjectorFactory",
"NoopNgZone",
"NotFoundError",
"NullInjector",
"ObjectUnsubscribedError",
"Observable",
@@ -259,6 +261,7 @@
"isInputBinding",
"isLContainer",
"isLView",
"isNotFound",
"isPositive",
"isPromise",
"isRefreshingViews",
@@ -88,6 +88,7 @@
"NG_INJ_DEF",
"NG_PIPE_DEF",
"NG_PROV_DEF",
"NOT_FOUND",
"NOT_FOUND2",
"NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR",
"NOT_YET",
@@ -101,6 +102,7 @@
"NodeInjectorFactory",
"NoneEncapsulationDomRenderer",
"NoopNgZone",
"NotFoundError",
"NullInjector",
"ObjectUnsubscribedError",
"Observable",
@@ -340,6 +342,7 @@
"isIterable",
"isLContainer",
"isLView",
"isNotFound",
"isPlatformServer",
"isPositive",
"isPromise",
@@ -15,8 +15,10 @@
"NG_INJECTOR_DEF",
"NG_INJ_DEF",
"NG_PROV_DEF",
"NOT_FOUND",
"NOT_YET",
"NULL_INJECTOR",
"NotFoundError",
"NullInjector",
"R3Injector",
"RuntimeError",
@@ -56,6 +58,7 @@
"internalImportProvidersFrom",
"isEnvironmentProviders",
"isFunction",
"isNotFound",
"isTypeProvider",
"isValueProvider",
"makeRecord",
@@ -123,6 +123,7 @@
"NG_MOD_DEF",
"NG_PIPE_DEF",
"NG_PROV_DEF",
"NOT_FOUND",
"NOT_FOUND2",
"NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR",
"NOT_YET",
@@ -151,6 +152,7 @@
"NodeInjectorFactory",
"NoneEncapsulationDomRenderer",
"NoopNgZone",
"NotFoundError",
"NullInjector",
"ObjectUnsubscribedError",
"Observable",
@@ -550,6 +552,7 @@
"isNavigationCancelingError",
"isNodeMatchingSelector",
"isNodeMatchingSelectorList",
"isNotFound",
"isPlatformServer",
"isPositive",
"isPromise",
@@ -76,6 +76,7 @@
"NG_INJ_DEF",
"NG_PIPE_DEF",
"NG_PROV_DEF",
"NOT_FOUND",
"NOT_FOUND2",
"NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR",
"NOT_YET",
@@ -89,6 +90,7 @@
"NodeInjectorFactory",
"NoneEncapsulationDomRenderer",
"NoopNgZone",
"NotFoundError",
"NullInjector",
"ObjectUnsubscribedError",
"Observable",
@@ -286,6 +288,7 @@
"isInputBinding",
"isLContainer",
"isLView",
"isNotFound",
"isPlatformServer",
"isPositive",
"isPromise",
@@ -84,6 +84,7 @@
"NG_MOD_DEF",
"NG_PIPE_DEF",
"NG_PROV_DEF",
"NOT_FOUND",
"NOT_FOUND2",
"NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR",
"NOT_YET",
@@ -104,6 +105,7 @@
"NodeInjectorFactory",
"NoneEncapsulationDomRenderer",
"NoopNgZone",
"NotFoundError",
"NullInjector",
"ObjectUnsubscribedError",
"Observable",
@@ -383,6 +385,7 @@
"isListLikeIterable",
"isNodeMatchingSelector",
"isNodeMatchingSelectorList",
"isNotFound",
"isPlatformServer",
"isPositive",
"isPromise",