#1 I have a type for the column that is an object. Column can be filterable or not, if isFilterable
is true
then the type Column
should require: filterType
, isTopBarFilter?
and options
(BUT only if filterType
is 'SELECT'
- #2).
type Column = {
name: string;
isFilterable: boolean; // passing here false should be equal with not passing the property at all (if possible)
// below properties should exist in type only if isFilterable = true
filterType: 'SELECT' | 'TEXT' | 'DATE';
options: string[]; // this property should exist in type only if filterType = 'SELECT'
isTopBarFilter?: boolean;
};
I do such type with use of types union and it work almost properly
type FilterableColumn = {
isFilterable: true;
filterType: 'SELECT' | 'TEXT' | 'DATE';
options: string[];
isTopBarFilter?: boolean;
};
type NonFilterableColumn = {
isFilterable: false;
};
type Column = (NonFilterableColumn | FilterableColumn) & {
name: string;
};
but:
Column
should require options
only if filterType
is 'SELECT'
. I have tried to do this with types union but it became works strange:type FilterableSelectColumn = {
filterType: 'SELECT';
options: string[];
};
type FilterableNonSelectColumn = {
filterType: 'TEXT' | 'DATE' | 'NUMBER';
};
type FilterableColumn = (FilterableSelectColumn | FilterableNonSelectColumn) & {
isFilterable: true;
isTopBarFilter?: boolean;
};
type NonFilterableColumn = {
isFilterable: false;
};
type Column = (FilterableColumn | NonFilterableColumn) & {
name: string;
};
// e.g
const col: Column = {
name: 'col2',
isFilterable: false,
filterType: 'SELECT', // unwanted
isTopBarFilter: false, // unwanted
options: ['option1'], // unwanted
};
If I set isFilterable
to false, TS doesn't suggesting unwanted properties (it is good) but also doesn't show error if I pass these unwanted props (it is bad)
isFilterable
even if it is false
, as I mentioned above I want to pass it only if it is true
Is there way to improve my solution(or another solution) to achieve what I described at the beginning (#1)?
Ok, after a few nights I managed to do it, I have two solutions:
1.
type FilterableColumn = {
isFilterable: true;
isTopBarFilter?: boolean;
} & (
| {
filterType: 'SELECT';
options: string[];
}
| {
filterType: 'TEXT' | 'DATE';
});
type NonFilterableColumn = {
isFilterable?: undefined; // same result with never
filterType?: undefined; // same result with never
};
type ColumnBaseFields = {
name: string;
};
type Column = (FilterableColumn | NonFilterableColumn) & ColumnBaseFields;
const column: Column = {
name: 'someName',
isFilterable: true,
filterType: 'SELECT',
options: ['option'],
};
It works as I wanted, Typescript errors appears for the cases, but error descriptions are inaccurate. I noticed that TypeScript works strange with many unions on the same nesting level
and because of it I made up the second solution with nested filters options
2.
type FilterSettings = (
| {
filterType: 'SELECT';
options: string[];
}
| {
filterType: 'TEXT';
}) & {
isTopBarFilter?: boolean;
};
type FilterableColumn = {
isFilterable: true;
filterSettings: FilterSettings;
};
type NonFilterableColumn = {
isFilterable?: undefined; // same result with never
};
type ColumnBaseFields = {
name: string;
};
type Column = (FilterableColumn | NonFilterableColumn) & ColumnBaseFields;
const column: Column = {
name: 'someName',
isFilterable: true,
filterSettings: {
filterType: 'SELECT',
options: ['option']
}
};
Works fine, typescript tell us exactly when some key is missed and when some key is unwanted.
I hope it will be helpful for someone