Question presented to candidate:
"Our app needs an ApiConfig object (base URL, retry count) available everywhere, and ApiConfig is a TypeScript interface. Why can't we just inject the interface, and what is an InjectionToken?"
What a strong answer should cover:
- DI looks values up by a runtime key; interfaces, type aliases and primitives are erased by TypeScript, so there is nothing to look up (the compiler reports NG2003 for an interface constructor parameter).
- An
InjectionToken<T>is a unique object used as that key, with a type parameter soinject(TOKEN)is typed asT; the string is only a description for error messages. - Provide a value for it with
{ provide: TOKEN, useValue | useFactory | useClass | useExisting }, or give it a default withnew InjectionToken('x', { providedIn: 'root', factory: () => ... }), which is tree-shakable. - Token identity is by object, not by description: two tokens with the same text are different keys.
- Common uses: config objects, feature flags, functions,
multi: trueextension points (HTTP_INTERCEPTORS,NG_VALUE_ACCESSOR), browser globals such asDOCUMENT. - Alternatives: an abstract class works as a token when you also want a contract with methods.
Clarifying questions expected:
- "Should there be a sensible default, or must every app supply the config?"
- "Will one part of the app need a different value?"
Code / implementation expected: Yes β a typed config token with a default, an override, and a multi token.