From 4a107ed7af26916c0505125c112add4718393ba7 Mon Sep 17 00:00:00 2001 From: iteriani Date: Tue, 18 Mar 2025 16:15:02 -0700 Subject: [PATCH] refactor(core): Return NOT_FOUND for retrieve instead of null (#60358) This fits within the spec of the retrieve. PR Close #60358 --- packages/core/primitives/di/src/not_found.ts | 2 +- .../core/src/di/injector_compatibility.ts | 32 +++++++++++++------ packages/core/src/di/null_injector.ts | 4 +-- packages/core/src/di/r3_injector.ts | 23 +++++++++---- .../bundle.golden_symbols.json | 3 ++ .../animations/bundle.golden_symbols.json | 3 ++ .../cyclic_import/bundle.golden_symbols.json | 3 ++ .../bundling/defer/bundle.golden_symbols.json | 3 ++ .../forms_reactive/bundle.golden_symbols.json | 3 ++ .../bundle.golden_symbols.json | 3 ++ .../hello_world/bundle.golden_symbols.json | 3 ++ .../hydration/bundle.golden_symbols.json | 3 ++ .../injection/bundle.golden_symbols.json | 3 ++ .../router/bundle.golden_symbols.json | 3 ++ .../bundle.golden_symbols.json | 3 ++ .../bundling/todo/bundle.golden_symbols.json | 3 ++ 16 files changed, 77 insertions(+), 20 deletions(-) diff --git a/packages/core/primitives/di/src/not_found.ts b/packages/core/primitives/di/src/not_found.ts index 2968fa307e8..6fe16345238 100644 --- a/packages/core/primitives/di/src/not_found.ts +++ b/packages/core/primitives/di/src/not_found.ts @@ -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'; } /** diff --git a/packages/core/src/di/injector_compatibility.ts b/packages/core/src/di/injector_compatibility.ts index f9173f08f95..f78e34acb87 100644 --- a/packages/core/src/di/injector_compatibility.ts +++ b/packages/core/src/di/injector_compatibility.ts @@ -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(token: PrimitivesInjectionToken, 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, - // 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, + // 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( } else if (currentInjector === null) { return injectRootLimpMode(token, undefined, flags); } else { - const value = currentInjector.retrieve( - token as PrimitivesInjectionToken, - convertToInjectOptions(flags), - ) as T; + const options = convertToInjectOptions(flags); + const value = currentInjector.retrieve(token as PrimitivesInjectionToken, options) as T; ngDevMode && emitInjectEvent(token as Type, value, flags); + if (isNotFound(value)) { + if (options.optional) { + return null; + } + throw value; + } return value; } } diff --git a/packages/core/src/di/null_injector.ts b/packages/core/src/di/null_injector.ts index daaafdea078..45615c35cb4 100644 --- a/packages/core/src/di/null_injector.ts +++ b/packages/core/src/di/null_injector.ts @@ -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; diff --git a/packages/core/src/di/r3_injector.ts b/packages/core/src/di/r3_injector.ts index fde7ee5cdab..28fab28722a 100644 --- a/packages/core/src/di/r3_injector.ts +++ b/packages/core/src/di/r3_injector.ts @@ -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(token: PrimitivesInjectionToken, options?: unknown): T | NotFound { const flags: InternalInjectFlags = convertToBitFlags(options as InjectOptions | undefined) || InternalInjectFlags.Default; - return (this as BackwardsCompatibleInjector).get( - token as unknown as InjectionToken, - // 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, + // 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) { diff --git a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json index dc3dfccc32f..91969236909 100644 --- a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json @@ -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", diff --git a/packages/core/test/bundling/animations/bundle.golden_symbols.json b/packages/core/test/bundling/animations/bundle.golden_symbols.json index 58e3c3e607a..03b8eeb033c 100644 --- a/packages/core/test/bundling/animations/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations/bundle.golden_symbols.json @@ -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", diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index 49612fb5f9c..ed3076e660b 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -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", diff --git a/packages/core/test/bundling/defer/bundle.golden_symbols.json b/packages/core/test/bundling/defer/bundle.golden_symbols.json index 70e29c735f8..afd76cbb9b7 100644 --- a/packages/core/test/bundling/defer/bundle.golden_symbols.json +++ b/packages/core/test/bundling/defer/bundle.golden_symbols.json @@ -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", diff --git a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json index df88c08b866..50cde688f71 100644 --- a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json @@ -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", diff --git a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json index 1153a61989c..3304ced04f3 100644 --- a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json @@ -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", diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 593ffecee29..abe7e2604f3 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -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", diff --git a/packages/core/test/bundling/hydration/bundle.golden_symbols.json b/packages/core/test/bundling/hydration/bundle.golden_symbols.json index e4a27e14287..a3cc7c074a6 100644 --- a/packages/core/test/bundling/hydration/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hydration/bundle.golden_symbols.json @@ -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", diff --git a/packages/core/test/bundling/injection/bundle.golden_symbols.json b/packages/core/test/bundling/injection/bundle.golden_symbols.json index 48397297144..67a3af21de1 100644 --- a/packages/core/test/bundling/injection/bundle.golden_symbols.json +++ b/packages/core/test/bundling/injection/bundle.golden_symbols.json @@ -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", diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 4a13c142cee..333cc0eae51 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -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", diff --git a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json index 7ea0d0e5f76..c57eb8e469d 100644 --- a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json +++ b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json @@ -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", diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 79143d41c7c..ff7c3a33bc0 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -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",