In a unique scenario, I am tasked with creating an object configuration for predefined filters using TypeScript generics.
Here is the current configuration:
const filterOptions: FilterOption[] = [
// Valid filters
{ field: 'name', operator: 'sw', operand: 'Mr.' },
{ field: 'age', operator: 'lt', operand: 18 },
{ field: 'joinDate', operator: 'gte', operand: new Date(new Date().setFullYear(new Date().getFullYear() - 1)) },
// Invalid filters
{ field: 'name', operator: 'eq', operand: 5 },
{ field: 'age', operator: 'in', operand: 5 },
];
Here are the types involved:
interface Filterable {
name: string;
age: number;
joinDate: Date;
}
type NumberOperator = 'lt' | 'lte' | 'gt' | 'gte' | 'eq' | 'ne';
type StringOperator = 'eq' | 'ne' | 'in' | 'ni' | 'sw' | 'ew';
type FilterOption = {
field: keyof Filterable,
operator: ???,
operand: ???,
}
I have two main questions:
operand
type - I need help defining this correctly using generics.operator
type - Is it possible to achieve this level of complexity with generics?
Currently, I am using a workaround to validate the configuration object, but I am seeking a cleaner solution using generics
interface FilterableString {
name: string;
}
interface FilterableNumber {
age: number;
}
interface FilterableDate {
joinDate: Date;
}
interface Filterable extends FilterableString, FilterableNumber, FilterableDate {}
type FilterOptionString = {
field: keyof FilterableString,
operator: StringOperator,
operand: string,
}
type FilterOptionNumber = {
field: keyof FilterableNumber,
operator: NumberOperator,
operand: number,
}
type FilterOptionDate = {
field: keyof FilterableDate,
operator: NumberOperator,
operand: Date,
}
type FilterOption = FilterOptionString | FilterOptionNumber | FilterOptionDate;
I aim to simplify the process with generics to handle potential future complexities and maintainability challenges.