📧 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

EasyAngularKit

Formation complète pour maîtriser Angular et développer des applications web modernes.

Navigation

Contact

Légal

© 2026 Easy Angular Kit. Tous droits réservés.