Skip to content

Instantly share code, notes, and snippets.

View alxhub's full-sized avatar

Alex Rickabaugh alxhub

  • Google, Inc.
  • San Francisco, CA
View GitHub Profile
function extendEnum(classType: any, enumType: any): void {
for (let key in enumType) {
classType[key] = enumType[key];
}
}
enum EnumType {
OPTION_A,
OPTION_B,
}

A good example is the AngularFireModule. Many of the Firebase services (auth, etc) may not be referenced from the application, and should be tree-shaken if needed. But they rely on the user providing a configuration token, and thus need to be in the same injector in which the user configures Firebase.

@NgModule({
  imports: [AngularFireModule.configureApp({...})]
})
class LazyLoadedModuleThatUsesFirebase {}
A good example is the AngularFireModule. Many of the Firebase services (auth, etc) may not be referenced from the application, and should be tree-shaken if needed. But they rely on the user providing a configuration token, and thus need to be in the same injector in which the user configures Firebase.
@NgModule({
imports: [AngularFireModule.configureApp({...})]
})
class LazyLoadedModuleThatUsesFirebase {}
In this case, the Firebase usage itself is lazily loaded, and the AngularFireAuth service should belong to the same injector in order to access the provided configuration.
@Injectable({
scope: AngularFireModule

Open Questions

How do we express scope/lifetime/etc?

At runtime, an application using DI has at least one injector, but possibly more than one (as a result of lazy loading). Injectors are created out of InjectorDefs.

There are two ways to declare that a token should be available in an injector:

  1. By listing it explicitly in an InjectorDef that's included in the injector.

Issue

Today, you can inject Injector. This means that a new IvyInjector (hypothetical name) has to detect requests to inject Injector, which would ordinarily be done by comparing the requested token to Injector by value.

This is undesirable because Injector statically references StaticInjector via Injector.create, so the above would cause StaticInjector to be pulled in to Ivy apps.

Proposed Solution

  1. Define an InjectionToken called INJECTOR. The IvyInjector looks for this requests for this token instead of Injector. This means that IvyInjector only has a type reference to Injector instead of a value reference, which fixes tree-shaking.
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Type} from '../type';
import {stringify} from '../util';
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {resolveForwardRef} from './forward_ref';
import {InjectionToken} from './injection_token'

Tree-shakeable Tokens Docs

Status quo and issues with it

Injector structure

Currently, to provide services in Angular, you include them in an @NgModule:

@Injectable()
@alxhub
alxhub / loading.ts
Created February 1, 2018 15:41
RXJS stream with loading events
// Loading indicator implementation.
interface StreamEvent<T> {
type: 'start' | 'done';
response?: T;
}
// Our starting Observable that will emit with each new Id.
declare let id$: Observable<Id>;