docs/api/types.md
qiankun provides comprehensive TypeScript type definitions to ensure type safety and excellent developer experience. This document covers all available types and interfaces.
Description: Base type for generic object structures.
export type ObjectType = Record<string, unknown>;
Usage:
// Used as a constraint for generic types
function processApp<T extends ObjectType>(props: T): void {
// T can be any object type
}
Description: Type for micro application entry points.
export type HTMLEntry = string;
Usage:
const appEntry: HTMLEntry = '//localhost:8080';
const appEntryWithPath: HTMLEntry = '//localhost:8080/micro-app';
Description: Base metadata for micro applications.
type AppMetadata = {
name: string; // Unique application name
entry: HTMLEntry; // Application entry URL
};
Description: Configuration for manually loaded micro applications.
export type LoadableApp<T extends ObjectType> = AppMetadata & {
container: HTMLElement; // DOM container element
props?: T; // Custom properties passed to the app
};
Usage:
// Basic usage
const app: LoadableApp<{}> = {
name: 'my-app',
entry: '//localhost:8080',
container: document.getElementById('app-container')!,
};
// With custom props
interface MyAppProps {
theme: 'light' | 'dark';
userId: string;
}
const appWithProps: LoadableApp<MyAppProps> = {
name: 'themed-app',
entry: '//localhost:8080',
container: document.getElementById('container')!,
props: {
theme: 'dark',
userId: '123'
}
};
Description: Configuration for route-based micro applications.
export type RegistrableApp<T extends ObjectType> = LoadableApp<T> & {
loader?: (loading: boolean) => void; // Loading state callback
activeRule: RegisterApplicationConfig['activeWhen']; // Routing activation rule
};
Usage:
import { registerMicroApps } from 'qiankun';
interface UserAppProps {
currentUser: { id: string; name: string };
}
const apps: RegistrableApp<UserAppProps>[] = [
{
name: 'user-dashboard',
entry: '//localhost:8001',
container: '#subapp-viewport',
activeRule: '/dashboard',
props: {
currentUser: { id: '123', name: 'John' }
},
loader: (loading) => {
if (loading) {
showLoadingSpinner();
} else {
hideLoadingSpinner();
}
}
}
];
registerMicroApps(apps);
Description: Configuration options for individual micro applications.
export type AppConfiguration = Partial<Pick<LoaderOpts, 'fetch' | 'streamTransformer' | 'nodeTransformer'>> & {
sandbox?: boolean; // Enable sandbox isolation
globalContext?: WindowProxy; // Custom global context
};
Usage:
import { loadMicroApp } from 'qiankun';
const customConfig: AppConfiguration = {
sandbox: true,
globalContext: window,
fetch: async (url, options) => {
// Custom fetch implementation
return fetch(url, {
...options,
headers: {
...options?.headers,
'Authorization': 'Bearer token'
}
});
},
nodeTransformer: (node, opts) => {
// Transform DOM nodes
if (node.tagName === 'SCRIPT') {
node.setAttribute('data-app', 'my-app');
}
return node;
}
};
loadMicroApp({
name: 'configured-app',
entry: '//localhost:8080',
container: document.getElementById('container')!
}, customConfig);
Description: Type for lifecycle hook functions.
export type LifeCycleFn<T extends ObjectType> = (
app: LoadableApp<T>,
global: WindowProxy
) => Promise<void>;
Usage:
const beforeLoadHook: LifeCycleFn<{ theme: string }> = async (app, global) => {
console.log(`Loading app: ${app.name}`);
global.__APP_THEME__ = app.props?.theme || 'default';
};
const afterMountHook: LifeCycleFn<any> = async (app, global) => {
console.log(`App ${app.name} mounted successfully`);
// Track analytics
analytics.track('app_mounted', { appName: app.name });
};
Description: Complete lifecycle hooks configuration.
export type LifeCycles<T extends ObjectType> = {
beforeLoad?: LifeCycleFn<T> | Array<LifeCycleFn<T>>;
beforeMount?: LifeCycleFn<T> | Array<LifeCycleFn<T>>;
afterMount?: LifeCycleFn<T> | Array<LifeCycleFn<T>>;
beforeUnmount?: LifeCycleFn<T> | Array<LifeCycleFn<T>>;
afterUnmount?: LifeCycleFn<T> | Array<LifeCycleFn<T>>;
};
Usage:
interface AppProps {
userId: string;
permissions: string[];
}
const lifecycles: LifeCycles<AppProps> = {
beforeLoad: async (app, global) => {
// Setup before loading
global.__USER_ID__ = app.props?.userId;
},
beforeMount: [
async (app, global) => {
// Multiple hooks as array
await setupAuthentication(app.props?.userId);
},
async (app, global) => {
await loadUserPermissions(app.props?.permissions);
}
],
afterMount: async (app) => {
console.log(`${app.name} is ready`);
},
beforeUnmount: async (app) => {
// Cleanup before unmounting
await saveUserState(app.name);
},
afterUnmount: async (app) => {
// Final cleanup
await clearUserData(app.name);
}
};
Description: Instance of a loaded micro application.
export type MicroApp = Parcel;
The MicroApp type extends the single-spa Parcel interface with these methods:
interface MicroApp {
mount(): Promise<void>; // Mount the application
unmount(): Promise<void>; // Unmount the application
update(props: any): Promise<void>; // Update application props
getStatus(): string; // Get current status
loadPromise: Promise<void>; // Promise that resolves when loaded
mountPromise: Promise<void>; // Promise that resolves when mounted
unmountPromise: Promise<void>; // Promise that resolves when unmounted
}
Usage:
import { loadMicroApp } from 'qiankun';
const microApp: MicroApp = loadMicroApp({
name: 'my-app',
entry: '//localhost:8080',
container: document.getElementById('container')!
});
// Check status
console.log(microApp.getStatus()); // 'LOADING', 'MOUNTED', 'UNMOUNTED', etc.
// Wait for mounting
await microApp.mountPromise;
console.log('App is mounted');
// Update props
await microApp.update({ newTheme: 'dark' });
// Unmount when done
await microApp.unmount();
Description: Internal lifecycle type used by qiankun.
export type MicroAppLifeCycles = FlattenArrayValue<ParcelLifeCycles<ExtraProps>>;
This type is primarily for internal use and represents the flattened lifecycle functions that micro applications must export.
qiankun extends the global Window interface with special properties:
declare global {
interface Window {
__POWERED_BY_QIANKUN__?: boolean; // Indicates app is running in qiankun
__INJECTED_PUBLIC_PATH_BY_QIANKUN__?: string; // Injected public path
__QIANKUN_DEVELOPMENT__?: boolean; // Development mode flag
Zone?: CallableFunction; // Zone.js compatibility
__zone_symbol__setTimeout?: Window['setTimeout']; // Zone.js timeout
}
}
Usage in Micro Applications:
// Check if running in qiankun
if (window.__POWERED_BY_QIANKUN__) {
console.log('Running as a micro app');
// Use injected public path
const publicPath = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ || '/';
// Configure your app accordingly
setupApp({ publicPath });
} else {
console.log('Running standalone');
setupApp({ publicPath: '/' });
}
Create type guards for better type safety:
// Type guard for LoadableApp
function isLoadableApp<T extends ObjectType>(
app: any
): app is LoadableApp<T> {
return (
typeof app === 'object' &&
typeof app.name === 'string' &&
typeof app.entry === 'string' &&
app.container instanceof HTMLElement
);
}
// Type guard for RegistrableApp
function isRegistrableApp<T extends ObjectType>(
app: any
): app is RegistrableApp<T> {
return (
isLoadableApp(app) &&
(typeof app.activeRule === 'string' || typeof app.activeRule === 'function')
);
}
// Usage
function processApp(app: unknown) {
if (isRegistrableApp(app)) {
// TypeScript knows app is RegistrableApp here
console.log(`Registering app: ${app.name} with rule: ${app.activeRule}`);
} else if (isLoadableApp(app)) {
// TypeScript knows app is LoadableApp here
console.log(`Loading app: ${app.name}`);
}
}
Create reusable generic types for common patterns:
// Props with theme support
type ThemedProps<T = {}> = T & {
theme?: 'light' | 'dark';
};
// Props with user context
type UserAwareProps<T = {}> = T & {
currentUser?: {
id: string;
name: string;
role: string;
};
};
// Combined props
type AppProps<T = {}> = ThemedProps<UserAwareProps<T>>;
// Usage
const app: LoadableApp<AppProps<{ customData: string }>> = {
name: 'themed-user-app',
entry: '//localhost:8080',
container: document.getElementById('container')!,
props: {
theme: 'dark',
currentUser: { id: '123', name: 'John', role: 'admin' },
customData: 'custom value'
}
};
// Configuration based on environment
type EnvironmentConfig<T extends 'development' | 'production'> = T extends 'development'
? {
sandbox: false;
prefetch: false;
strictStyleIsolation: false;
}
: {
sandbox: true;
prefetch: 'all';
strictStyleIsolation: true;
};
// Usage with environment detection
declare const NODE_ENV: 'development' | 'production';
type CurrentConfig = EnvironmentConfig<typeof NODE_ENV>;
// Create branded type for app names to prevent mix-ups
type AppName = string & { readonly __brand: unique symbol };
function createAppName(name: string): AppName {
return name as AppName;
}
// Enhanced LoadableApp with branded name
type SafeLoadableApp<T extends ObjectType> = Omit<LoadableApp<T>, 'name'> & {
name: AppName;
};
// Usage
const appName = createAppName('my-secure-app');
const app: SafeLoadableApp<{}> = {
name: appName, // Type-safe app name
entry: '//localhost:8080',
container: document.getElementById('container')!
};
// Enhanced lifecycle with event data
type LifeCycleEvent<T extends ObjectType> = {
app: LoadableApp<T>;
global: WindowProxy;
timestamp: number;
phase: 'beforeLoad' | 'beforeMount' | 'afterMount' | 'beforeUnmount' | 'afterUnmount';
};
type EnhancedLifeCycleFn<T extends ObjectType> = (event: LifeCycleEvent<T>) => Promise<void>;
// Usage
const enhancedHook: EnhancedLifeCycleFn<{ userId: string }> = async (event) => {
console.log(`Phase: ${event.phase}, App: ${event.app.name}, Time: ${event.timestamp}`);
if (event.phase === 'beforeMount') {
// Setup user context
event.global.__USER_ID__ = event.app.props?.userId;
}
};
// Helper function with automatic type inference
function createTypedApp<T extends ObjectType>(
config: {
name: string;
entry: string;
container: HTMLElement;
props: T;
}
): LoadableApp<T> {
return config; // TypeScript infers the correct type
}
// Usage - TypeScript automatically infers the props type
const app = createTypedApp({
name: 'inferred-app',
entry: '//localhost:8080',
container: document.getElementById('container')!,
props: {
theme: 'dark',
userId: '123',
features: ['feature1', 'feature2']
}
// TypeScript knows props type is { theme: string; userId: string; features: string[] }
});
// Helper for creating typed lifecycles
function createLifecycles<T extends ObjectType>(
lifecycles: LifeCycles<T>
): LifeCycles<T> {
return lifecycles;
}
// Usage with inference
const typedLifecycles = createLifecycles({
beforeMount: async (app) => {
// TypeScript infers app.props type based on usage
console.log(app.props?.theme); // TypeScript knows this might be undefined
}
});
// ✅ Good: Strict typing
interface StrictAppProps {
readonly userId: string;
readonly theme: 'light' | 'dark';
readonly permissions: readonly string[];
}
const app: LoadableApp<StrictAppProps> = {
name: 'strict-app',
entry: '//localhost:8080',
container: document.getElementById('container')!,
props: {
userId: '123',
theme: 'dark',
permissions: ['read', 'write']
}
};
// ❌ Bad: Loose typing
const looseApp: LoadableApp<any> = {
name: 'loose-app',
entry: '//localhost:8080',
container: document.getElementById('container')!,
props: { anything: 'goes' } // No type safety
};
// Create types specific to your domain
interface ECommerceAppProps {
cartId: string;
currency: 'USD' | 'EUR' | 'GBP';
customerSegment: 'premium' | 'standard';
features: {
wishlist: boolean;
recommendations: boolean;
reviews: boolean;
};
}
type ECommerceApp = LoadableApp<ECommerceAppProps>;
type ECommerceLifecycles = LifeCycles<ECommerceAppProps>;
// Constrain generic types for better type safety
interface BaseAppProps {
version: string;
environment: 'development' | 'staging' | 'production';
}
function createApp<T extends BaseAppProps>(
config: Omit<LoadableApp<T>, 'container'> & {
containerId: string;
}
): LoadableApp<T> {
const container = document.getElementById(config.containerId);
if (!container) {
throw new Error(`Container ${config.containerId} not found`);
}
return {
...config,
container
};
}