📧 Reste informé(e) !

Reçois les derniers articles et conseils EasyAngularKit directement dans ta boîte mail.

S'inscrire gratuitement

~3 min de lecture

InjectionToken tree-shakable : arrête de polluer ton bundle

Tu crées des InjectionToken pour injecter des configurations, des valeurs ou des APIs natives. Mais sais-tu que la façon dont tu les déclares peut empêcher le tree-shaking et alourdir ton bundle inutilement ?

Le problème : le token classique

Voici comment beaucoup de développeurs créent leurs tokens :

// api-url.token.ts
export const API_URL = new InjectionToken<string>('API_URL');

Puis, quelque part dans l'application :

// app.config.ts
export const appConfig: ApplicationConfig = {
    providers: [
        {provide: API_URL, useValue: 'https://api.example.com'}
    ]
};

Le problème ? Ce token est fourni dans le tableau providers. Même si aucun composant ou service ne l'utilise, il sera inclus dans le bundle final. Le tree-shaker ne peut pas l'éliminer, car il y a une référence explicite dans la configuration.

La solution : providedIn + factory

Tu peux rendre tes tokens tree-shakable en déclarant directement leur valeur :

// api-url.token.ts
export const API_URL = new InjectionToken<string>('API_URL', {
    providedIn: 'root',
    factory: () => 'https://api.example.com'
});

C'est tout. Plus besoin de l'ajouter dans providers. Angular le fournit automatiquement au niveau root, et **surtout ** : si personne ne l'injecte, il disparaît du bundle.

Comment ça marche ?

Le tree-shaking repose sur l'analyse statique des imports et des références. Quand tu mets un provider dans un tableau, tu crées une référence explicite — le bundler doit l'inclure.

Avec providedIn: 'root', il n'y a pas de référence dans un module ou une config. Angular résout la dépendance au runtime via la factory. Si aucun code n'appelle inject(API_URL), le token et sa factory sont éliminés.

// ❌ Non tree-shakable : référence explicite dans providers
providers: [{provide: TOKEN, useValue: 'value'}]

// ✅ Tree-shakable : résolu à la demande
new InjectionToken('...', {providedIn: 'root', factory: () => 'value'})

Injecter des dépendances dans la factory

La vraie puissance arrive quand ta factory a besoin d'autres dépendances. Utilise inject() :

import {InjectionToken, inject} from '@angular/core';
import {DOCUMENT} from '@angular/common';

export const WINDOW = new InjectionToken<Window>('Window API', {
    providedIn: 'root',
    factory: () => inject(DOCUMENT).defaultView!
});

export const LOCAL_STORAGE = new InjectionToken<Storage>('LocalStorage API', {
    providedIn: 'root',
    factory: () => inject(WINDOW).localStorage
});

Tu peux chaîner les dépendances. Ici, LOCAL_STORAGE dépend de WINDOW, qui dépend de DOCUMENT. Tout reste tree-shakable.

Cas d'usage concrets

Configuration applicative

type AppConfig = {
    apiUrl: string;
    debug: boolean;
    maxRetries: number;
};

export const APP_CONFIG = new InjectionToken<AppConfig>('App Configuration', {
    providedIn: 'root',
    factory: () => ({
        apiUrl: 'https://api.prod.example.com',
        debug: false,
        maxRetries: 3
    })
});

Feature flags basés sur l'environnement

import {InjectionToken, inject, isDevMode} from '@angular/core';

export const ENABLE_ANALYTICS = new InjectionToken<boolean>('Analytics Flag', {
    providedIn: 'root',
    factory: () => !isDevMode()
});

Abstraction d'APIs navigateur

export const INTERSECTION_OBSERVER = new InjectionToken<typeof IntersectionObserver>(
    'IntersectionObserver API',
    {
        providedIn: 'root',
        factory: () => inject(WINDOW).IntersectionObserver
    }
);

Ça facilite le mocking en tests et le SSR.

Le piège : instances multiples

Attention critique : utilise toujours la même instance du token.

// ❌ ERREUR : deux instances différentes
// tokens.ts
export const MY_TOKEN = new InjectionToken<string>('MyToken', {
    providedIn: 'root',
    factory: () => 'value'
});

// autre-fichier.ts
const MY_TOKEN = new InjectionToken<string>('MyToken'); // Nouvelle instance !
// inject(MY_TOKEN) → NullInjectorError 💥

Chaque appel à new InjectionToken() crée un token distinct, même avec la même description. Angular les traite comme des clés différentes.

Solution : exporte et importe toujours le même token depuis un fichier dédié.

Surcharger un token tree-shakable

Tu peux toujours override un token tree-shakable dans providers si besoin :

// Le token avec sa valeur par défaut
export const API_URL = new InjectionToken<string>('API_URL', {
    providedIn: 'root',
    factory: () => 'https://api.prod.example.com'
});

// Override pour un environnement spécifique
export const appConfig: ApplicationConfig = {
    providers: [
        {provide: API_URL, useValue: 'https://api.staging.example.com'}
    ]
};

La factory sert de fallback. Si tu fournis explicitement le token, ta valeur prend le dessus.

Tests : TestBed.overrideProvider

Pour les tests, utilise overrideProvider avant la création du composant :

beforeEach(() => {
    TestBed.overrideProvider(API_URL, {useValue: 'https://api.test.local'});

    TestBed.configureTestingModule({
        imports: [MyComponent]
    });
});

Les options de providedIn

Valeur Comportement
'root' Singleton au niveau application (recommandé)
'platform' Partagé entre applications (cas rare, micro-frontends)
null / non spécifié Doit être fourni manuellement
'any' Déprécié — nouvelle instance par lazy module
NgModule Déprécié — utilise 'root' à la place

En résumé

Approche Tree-shakable Quand l'utiliser
providers: [{ provide, useValue }] ❌ Non Override explicite, valeurs dynamiques au bootstrap
new InjectionToken(..., { providedIn, factory }) ✅ Oui Valeurs par défaut, configurations, APIs

La règle : déclare tes tokens avec providedIn: 'root' et une factory par défaut. Override uniquement quand c'est nécessaire.

// ✅ Pattern recommandé
export const MY_TOKEN = new InjectionToken<MyType>('Description', {
    providedIn: 'root',
    factory: () => defaultValue
});

Tes tokens deviennent tree-shakable, testables, et tu n'as plus à les déclarer dans chaque module. Moins de boilerplate, bundle plus léger.

📧 Reste informé(e) !

Reçois les derniers articles et conseils EasyAngularKit directement dans ta boîte mail.

S'inscrire gratuitement

AngularKit

Suite d'outils pour développeurs Angular francophones. Apprends, modernise tes réflexes, audite ta codebase.

Produits

Contact

Légal

© 2026 AngularKit. Tous droits réservés.