I would create an abstract class that would be inherited to match every application needs.
There would be a PRODUCT_CONFIG
by default, that could be also extended.
In the following code, getConfig()
returns the correct type of value depending on the configured key.
warning : The annoying thing is that the user of ProductConfigService
have to specify the name of the key
used for the configuration. I don't know how to extract it from the parameter itself.
playground
// ---------- ABSTRACT CLASS DECLARATION
interface PRODUCT_CONFIG {
API_KEY: string;
AGE: number;
TEXT: string;
}
abstract class AProductConfigService<T extends PRODUCT_CONFIG, U extends keyof T> {
protected product: T;
protected accessKey: keyof T;
constructor(accessKey: keyof T, product: T) {
this.accessKey = accessKey
this.product = product;
}
public getConfig(): T[U] {
return this.product[this.accessKey] as T[U];
}
}
// ---------- CLASS IMPLEMENTATION
interface EXTENDED_PRODUCT_CONFIG extends PRODUCT_CONFIG {
// additionnal keys ...
ANIMAL: string;
}
class ProductConfigService<U extends keyof EXTENDED_PRODUCT_CONFIG> extends AProductConfigService<EXTENDED_PRODUCT_CONFIG, U> {
constructor(accessKey: U) {
super(accessKey, product2);
}
// ... new methods
}
// ---------- PRODUCTS DEFINITION
const product1: PRODUCT_CONFIG = {
API_KEY: 'SOME_VALUE',
AGE: 10,
TEXT: 'SOME_VALUE',
}
const product2: EXTENDED_PRODUCT_CONFIG = {
API_KEY: 'SOME_VALUE',
AGE: 12,
TEXT: 'SOME_VALUE',
ANIMAL: 'fox',
}
// -------------- PRODUCT USE
const productA = new ProductConfigService<'AGE'>('AGE');
const valA = productA.getConfig();
console.log(valA);
const productB = new ProductConfigService<'API_KEY'>('API_KEY');
const valB = productB.getConfig();
console.log(valB);
const productC = new ProductConfigService<'ANIMAL'>('ANIMAL');
const valC = productC.getConfig();
console.log(valC);
// ----------------
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…