Help me implementeren van een type-veilige configuratiebeheeroplossing in TypeScript voor {{config_type}}.
description
Door het implementeren van een type-veilige configuratiebeheeroplossing, verbeter je de betrouwbaarheid van je toepassing, verminder je runtime-fouten, en zorg je dat je configuraties correct getypeerd en gevalideerd worden, wat leidt tot soepelere ontwikkeling en onderhoudsprocessen.
prompt
try_prompt
Geef begeleiding over hoe je een robuust configuratiebeheersysteem in TypeScript kunt maken dat typeveiligheid garandeert voor {{config_type}}. Inclusief voorbeelden va ... more
generate_helper
...
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Het opzetten van een robuust configuratiebeheersysteem in TypeScript dat typeveiligheid garandeert voor omgevingsvariabelen vereist enkele stappen: het definiëren van types, het laden van configuraties uit verschillende bronnen, en runtime validatie. Hier is een gestructureerde aanpak met voorbeelden:
1. Definieer je configuratietypes
Maak een duidelijk en strikt type voor je configuratie. Bijvoorbeeld:
```typescript
// types.ts
export interface Config {
PORT: number;
DATABASE_URL: string;
ENABLE_FEATURE_X: boolean;
LOG_LEVEL: 'debug' | 'info' | 'warn' | 'error';
}
```
2. Laad en parse omgevingsvariabelen
Gebruik `process.env` en converteer de waarden naar de juiste types. Dit doe je meestal met helper functies:
```typescript
// loadConfig.ts
import { Config } from './types';
function parseNumber(value: string | undefined, defaultValue?: number): number {
const parsed = Number(value);
if (isNaN(parsed)) {
if (defaultValue !== undefined) return defaultValue;
throw new Error(`Kan "${value}" niet parseren naar een nummer.`);
}
return parsed;
}
function parseBoolean(value: string | undefined): boolean {
return value === 'true';
}
function parseLogLevel(value: string | undefined): 'debug' | 'info' | 'warn' | 'error' {
const levels = ['debug', 'info', 'warn', 'error'] as const;
if (value && levels.includes(value)) {
return value as typeof levels[number];
}
return 'info'; // of throw een error
}
export function loadConfig(): Config {
return {
PORT: parseNumber(process.env.PORT),
DATABASE_URL: process.env.DATABASE_URL || '',
ENABLE_FEATURE_X: parseBoolean(process.env.ENABLE_FEATURE_X),
LOG_LEVEL: parseLogLevel(process.env.LOG_LEVEL),
};
}
```
3. Validatie en typeveiligheid
Gebruik bijvoorbeeld `zod` of `io-ts` voor runtime validatie, zodat je zeker bent dat de configuratie correct is:
```typescript
// validateConfig.ts
import { z } from 'zod';
const ConfigSchema = z.object({
PORT: z.number(),
DATABASE_URL: z.string().nonempty(),
ENABLE_FEATURE_X: z.boolean(),
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']),
});
export function validateConfig(config: any) {
return ConfigSchema.parse(config);
}
```
4. Integratie en gebruik
Combineer alles in je hoofdapplicatie:
```typescript
// main.ts
import { loadConfig } from './loadConfig';
import { validateConfig } from './validateConfig';
const rawConfig = loadConfig();
try {
const config = validateConfig(rawConfig);
// gebruik config veilig
} catch (e) {
console.error('Configuratiefout:', e);
process.exit(1);
}
```
**Samenvatting:**
- Definieer expliciete types voor je configuratie.
- Laad omgevingsvariabelen en converteer ze op type-niveau.
- Gebruik een runtime validatietool zoals `zod` om de configuratie te controleren.
- Zorg dat fouten tijdens de startup worden afgehandeld, zodat je niet met ongeldige configuratie draait.
Met deze aanpak garandeer je dat je configuraties typeveilig en correct gevalideerd zijn, zowel compileer- als runtime.
provider: deepseek
model: deepseek-chat
Hier is een uitgebreide gids voor het creëren van een robuust configuratiebeheersysteem in TypeScript:
## 1. Basis Configuratie Type Definitie
```typescript
// types/config.ts
interface DatabaseConfig {
host: string;
port: number;
username: string;
password: string;
database: string;
}
interface ApiConfig {
baseUrl: string;
timeout: number;
apiKey: string;
}
interface AppConfig {
environment: 'development' | 'staging' | 'production';
port: number;
database: DatabaseConfig;
api: ApiConfig;
logLevel: 'error' | 'warn' | 'info' | 'debug';
}
```
## 2. Schema Validatie met Zod
```typescript
// validation/configSchema.ts
import { z } from 'zod';
const DatabaseSchema = z.object({
host: z.string().min(1, "Database host is verplicht"),
port: z.number().int().min(1).max(65535),
username: z.string().min(1),
password: z.string().min(1),
database: z.string().min(1)
});
const ApiSchema = z.object({
baseUrl: z.string().url(),
timeout: z.number().int().positive(),
apiKey: z.string().min(1)
});
const AppConfigSchema = z.object({
environment: z.enum(['development', 'staging', 'production']),
port: z.number().int().min(1).max(65535),
database: DatabaseSchema,
api: ApiSchema,
logLevel: z.enum(['error', 'warn', 'info', 'debug'])
});
```
## 3. Configuratie Loader met Type Safety
```typescript
// config/ConfigLoader.ts
import { z } from 'zod';
import { AppConfigSchema } from '../validation/configSchema';
import type { AppConfig } from '../types/config';
class ConfigLoader {
private config: AppConfig | null = null;
async load(): Promise<AppConfig> {
if (this.config) {
return this.config;
}
// Laad configuratie uit verschillende bronnen
const rawConfig = await this.loadFromMultipleSources();
// Valideer de configuratie
const validatedConfig = this.validateConfig(rawConfig);
this.config = validatedConfig;
return validatedConfig;
}
private async loadFromMultipleSources(): Promise<unknown> {
// 1. Environment variabelen
const envConfig = this.loadFromEnv();
// 2. Configuratiebestanden (optioneel)
const fileConfig = await this.loadFromFile();
// 3. Default waarden
const defaultConfig = this.getDefaultConfig();
return {
...defaultConfig,
...fileConfig,
...envConfig
};
}
private loadFromEnv(): Partial<AppConfig> {
return {
environment: process.env.NODE_ENV as AppConfig['environment'],
port: process.env.PORT ? parseInt(process.env.PORT) : undefined,
database: {
host: process.env.DB_HOST,
port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : undefined,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
},
api: {
baseUrl: process.env.API_BASE_URL,
timeout: process.env.API_TIMEOUT ? parseInt(process.env.API_TIMEOUT) : undefined,
apiKey: process.env.API_KEY
},
logLevel: process.env.LOG_LEVEL as AppConfig['logLevel']
};
}
private async loadFromFile(): Promise<Partial<AppConfig>> {
try {
// Voor development: laad uit config file
if (process.env.NODE_ENV === 'development') {
const config = await import('../../config/development.json');
return config.default || config;
}
} catch (error) {
// Negeer als config file niet bestaat
}
return {};
}
private getDefaultConfig(): Partial<AppConfig> {
return {
port: 3000,
logLevel: 'info',
api: {
timeout: 5000
}
};
}
private validateConfig(rawConfig: unknown): AppConfig {
try {
return AppConfigSchema.parse(rawConfig) as AppConfig;
} catch (error) {
if (error instanceof z.ZodError) {
const errorMessages = error.errors.map(err =>
`${err.path.join('.')}: ${err.message}`
).join('\n');
throw new Error(`Configuratie validatie mislukt:\n${errorMessages}`);
}
throw error;
}
}
getConfig(): AppConfig {
if (!this.config) {
throw new Error('Configuratie niet geladen. Roep load() aan eerst.');
}
return this.config;
}
}
export const configLoader = new ConfigLoader();
```
## 4. Gebruik in de Applicatie
```typescript
// app.ts
import { configLoader } from './config/ConfigLoader';
async function bootstrap() {
try {
// Laad en valideer configuratie bij opstarten
const config = await configLoader.load();
console.log('Configuratie succesvol geladen:');
console.log(`Environment: ${config.environment}`);
console.log(`Database: ${config.database.host}:${config.database.port}`);
// Start je applicatie met de gevalideerde configuratie
startApplication(config);
} catch (error) {
console.error('Fout bij laden configuratie:', error.message);
process.exit(1);
}
}
function startApplication(config: AppConfig) {
// Je applicatie logica hier
console.log(`Applicatie gestart op poort ${config.port}`);
}
bootstrap();
```
## 5. Geavanceerde Features
### Environment-specifieke configuratie:
```typescript
// config/EnvironmentConfig.ts
import { z } from 'zod';
const EnvironmentSchema = z.object({
NODE_ENV: z.enum(['development', 'staging', 'production']),
PORT: z.string().transform(val => parseInt(val, 10)),
DB_HOST: z.string(),
DB_PORT: z.string().transform(val => parseInt(val, 10)),
DB_USERNAME: z.string(),
DB_PASSWORD: z.string(),
DB_NAME: z.string(),
API_BASE_URL: z.string().url(),
API_TIMEOUT: z.string().transform(val => parseInt(val, 10)),
API_KEY: z.string(),
LOG_LEVEL: z.enum(['error', 'warn', 'info', 'debug'])
});
export class EnvironmentConfig {
static validate() {
return EnvironmentSchema.parse(process.env);
}
}
```
### Configuratie met transformaties:
```typescript
// config/AdvancedConfigLoader.ts
import { z } from 'zod';
const AdvancedAppConfigSchema = AppConfigSchema.extend({
database: DatabaseSchema.extend({
// Transformeer host naar lowercase
host: z.string().transform(host => host.toLowerCase()),
// Valideer database naam
database: z.string().regex(/^[a-zA-Z0-9_]+$/, "Ongeldige database naam")
}),
api: ApiSchema.extend({
// Zorg ervoor dat baseUrl eindigt met /
baseUrl: z.string().transform(url =>
url.endsWith('/') ? url : `${url}/`
)
})
});
```
## 6. Testen
```typescript
// tests/ConfigLoader.test.ts
import { configLoader } from '../config/ConfigLoader';
describe('ConfigLoader', () => {
beforeEach(() => {
// Reset process.env voor elke test
delete process.env.NODE_ENV;
delete process.env.DB_HOST;
// ... reset andere env vars
});
it('should load valid configuration', async () => {
process.env.NODE_ENV = 'development';
process.env.DB_HOST = 'localhost';
process.env.DB_PORT = '5432';
process.env.DB_USERNAME = 'user';
process.env.DB_PASSWORD = 'pass';
process.env.DB_NAME = 'mydb';
process.env.API_BASE_URL = 'https://api.example.com';
process.env.API_TIMEOUT = '5000';
process.env.API_KEY = 'test-key';
process.env.LOG_LEVEL = 'info';
const config = await configLoader.load();
expect(config.database.host).toBe('localhost');
expect(config.environment).toBe('development');
});
it('should throw error for invalid configuration', async () => {
process.env.NODE_ENV = 'invalid'; // Ongeldige waarde
await expect(configLoader.load()).rejects.toThrow();
});
});
```
## Belangrijke Voordelen:
1. **Type Safety**: Volledige TypeScript ondersteuning
2. **Runtime Validatie**: Zod zorgt voor validatie tijdens runtime
3. **Flexibele Bronnen**: Ondersteunt multiple configuratiebronnen
4. **Vroegtijdige Foutdetectie**: Configuratiefouten worden direct gedetecteerd
5. **Documentatie**: Schema's dienen als documentatie
6. **Testbaar**: Eenvoudig te testen met verschillende configuraties
Dit systeem garandeert dat je applicatie altijd start met een geldige configuratie en voorkomt runtime fouten door ontbrekende of ongeldige configuratiewaarden.

