So, I have enums:
export enum FilterName {
Date = 'date',
State = 'state',
}
export enum FilterField {
CreatedAtStartDate = 'createdAtStartDate',
CreatedAtEndDate = 'createdAtEndDate'
State = 'state',
}
export type TDateFields = {
min: FilterField.CreatedAtStartDate;
max: FilterField.CreatedAtEndDate;
};
I have function:
export const getFilterField = (filterName: FilterName) =>
({
[FilterName.Date]: {
min: FilterField.CreatedAtStartDate,
max: FilterField.CreatedAtEndDate,
} as TDateFields,
[FilterName.State]: FilterField.State,
}[filterName]);
Now, I call the function like this:
const filterFields = getFilterField(FilterName.Date);
Doing filterFields.min
will throw an error:
Property 'min' does not exist on type 'FilterField | TDateFields'
I can solve this by doing const filterFields = getFilterField(filterName) as TDateFields
, but I would like to have type narrowing. Is that possible in a case like this and if yes, how?
CodePudding user response:
You can restrict your JSON key/value object to a type, and for a bonus, use generics to change the return type depending on the enum type.
export enum FilterName {
Date = 'date',
State = 'state',
}
export enum FilterField {
CreatedAtStartDate = 'createdAtStartDate',
CreatedAtEndDate = 'createdAtEndDate',
State = 'state',
}
export type TDateFields = {
min: FilterField.CreatedAtStartDate;
max: FilterField.CreatedAtEndDate;
};
interface FieldData {
[FilterName.Date]: TDateFields,
[FilterName.State]: FilterField.State
}
export function getFilterField<T extends FilterName>(filterName: T): FieldData[T] {
const data: FieldData = {
[FilterName.Date]: {
min: FilterField.CreatedAtStartDate,
max: FilterField.CreatedAtEndDate,
} as TDateFields,
[FilterName.State]: FilterField.State,
}
return data[filterName]
}
const filterFields = getFilterField(FilterName.Date);
let min = filterFields.min
// ^? let min: FilterFIelds.CreatedAtStartData
CodePudding user response:
You can have no need for casting by using an overload:
export enum FilterName {
Date = 'date',
State = 'state',
}
export enum FilterField {
CreatedAtStartDate = 'createdAtStartDate',
CreatedAtEndDate = 'createdAtEndDate',
State = 'state',
}
export type TDateFields = {
min: FilterField.CreatedAtStartDate;
max: FilterField.CreatedAtEndDate;
};
export function getFilterField (filterName: FilterName.Date): TDateFields
export function getFilterField (filterName: FilterName.State): { [FilterName.State]: FilterField.State }
export function getFilterField (filterName: FilterName) {
switch (filterName) {
case FilterName.Date:
return {
min: FilterField.CreatedAtStartDate,
max: FilterField.CreatedAtEndDate,
};
case FilterName.State:
return { [FilterName.State]: FilterField.State }
// no need for a default case, the compiler understands
// this is exhaustive
}
};
// Has type TDateFields
const filterFields = getFilterField(FilterName.Date);
// No type error, compiler already narrowed during
// overload resolution.
filterFields.min;
I took the liberty of adding the missing member of the one enum (see my comment on the question). Note also that there's no cast on the TDateFields return case anymore.