Reputation: 59475
I am struggling creating a parent class for this "normalizer" type. I have a couple of classes that all share this same class structure. I'd love for a way to have the EmailNormalizer
just contain some of the logic inside the constructor, and have all the common functionality tucked away in the parent class.
I have this email normalizer class:
export class EmailNormalizer {
readonly TYPE: string | EmailNormalizer;
normalized: string;
constructor(raw: EmailNormalizer['TYPE']) {
if (!raw) throw new Error('Invalid Email');
if (raw instanceof EmailNormalizer) {
this.normalized = raw.normalized;
} else {
const results = email.analyze(raw);
if (results) throw new Error(`Email ${results.error}`);
this.normalized = _normalizeEmail(raw);
}
}
static normalize(raw: EmailNormalizer['TYPE']) {
return new EmailNormalizer(raw).normalized;
}
}
You can use it two ways
new EmailNormalizer('[email protected]').normalize
EmailNormalizer.normalize('[email protected]')
I am trying to create a shared "normalizer" parent class so I don't forget any of the pieces.
This was my first attempt. I am running into issues with the static method, and then I ran into issues when I attempted to make a more generic normalizer method.
class Normalizer<In = unknown, Out = unknown> {
readonly TYPE: In;
normalized: Out;
normalize: (raw: In) => Out;
constructor(raw: In) {
this.normalized = this.normalize(raw);
}
static normalize<In, T extends typeof Normalizer>(this: T, raw: In) {
return new this(raw).normalized;
}
}
export class EmailNormalizer extends Normalizer<EmailNormalizer | string, string> {
normalize = (raw: Email['TYPE']) => {
if (raw instanceof Email) return raw.normalized;
if (!raw) throw new Error('Invalid Email');
const results = email.analyze(raw);
if (results) throw new Error(`Email ${results.error}`);
return _normalizeEmail(raw);
};
}
function normalize<I extends any, O extends any, T extends Normalizer<I, O>>(i: T, value) {
return new i(value).normalized;
}
// ideally
EmailNormalizer.normalize('[email protected]');
// or
normalize(EmailNormalizer, '[email protected]')
Can anyone help design a normalize parent class?
Does this parent class need to be abstract?
Upvotes: 0
Views: 42
Reputation: 7428
I could suggest considering using composition over inheritance. For example, some strategy pattern would be helpful here:
interface NormalizerStrategy<In, Out> {
normalize: (raw: In) => Out;
}
class EmailNormalizerStrategy implements NormalizerStrategy<string, string> {
normalize(raw: string): string {
// pretend like there is some processing here
const result = raw;
return result;
}
}
class AgeNormalizerStrategy implements NormalizerStrategy<string, number> {
normalize(raw: string): number {
// pretend like there is some processing here
const result = Number(raw);
return result;
}
}
class Normalizer<In, Out> {
constructor(private readonly strategy: NormalizerStrategy<In, Out>) {}
normalize(input: In): Out {
this.someProcessingCommonToAllTheStrategies(input);
return this.strategy.normalize(input);
}
private someProcessingCommonToAllTheStrategies(data: In): void {
// pretend like there is some processing here
}
}
const emailNormalizer = new Normalizer(new EmailNormalizerStrategy);
const normalizedEmail: string = emailNormalizer.normalize('some email');
const ageNormalizer = new Normalizer(new AgeNormalizerStrategy);
const normalizedAge: number = ageNormalizer.normalize('some age');
Upvotes: 2