I have been working on creating a straightforward parser that relies on rules provided by the constructor. Everything seems to be running smoothly, but there are some issues with data types.
interface RuleBase {
parse(text: string, params: Record<string, any>): string;
}
class RuleA implements RuleBase {
parse(text: string, params: { a: 'aa' }) {
// parsing rules
return '';
}
}
class RuleB implements RuleBase {
parse(text: string, params: { b: 'bb' }) {
// parsing rules
return '';
}
}
class Parser<T extends Record<string, RuleBase>> {
private parserRules: T;
constructor(parserRules: T) {
this.parserRules = parserRules;
}
parse(text: string, rules: { ruleName: keyof T; params: Parameters<T[keyof T]['parse']>[1] }[]) {
let result = text;
rules.forEach(({ ruleName, params }) => {
const rule = this.parserRules[ruleName];
if (rule) result = rule.parse(result, params);
});
return result;
}
}
Here is an example of how it can be used:
const parser = new Parser({
a: new RuleA(),
b: new RuleB(),
});
// Example with correct parameters
parser.parse('Example1', [
{ ruleName: 'a', params: { a: 'aa' } },
{ ruleName: 'b', params: { b: 'bb' } },
]);
parser.parse('Example2', [
{ ruleName: 'b', params: { b: 'bb' } },
]);
// This will throw an error because 'b' only expects the parameter 'b'
parser.parse('Example3', [
{ ruleName: 'b', params: { a: 'aa', b: 'bb' } },
]);
// This will also throw an error as both rules have specific parameters they require
parser.parse('Example4', [
{ ruleName: 'a', params: { a: 'aa', b: 'bb' } },
{ ruleName: 'b', params: { a: 'aa', b: 'bb' } },
]);
In the case of Example3, the rule 'b' should only accept { b: 'bb' } for its parameters, but currently it allows other combinations unintentionally.
Is there a way to address this issue without altering the code's logic? Or should I consider revising the approach?