Looking for a way to create typings for the code I am working on. Let's say I have the following interfaces:
interface Car {
id: string;
type: number
ownerId: number | undefined;
// ... other fields
}
interface Plane {
id: number;
ownerId: number | undefined;
// ... other fields
}
These interfaces are frequently used with Array
methods in the following manner:
const fCars = cars
.filter(car => car.ownerId !== undefined)
.filter(car => car.type === 1)
const fPlanes = planes
.filter(plane => plane.ownerId === 1)
// fCars and fPlanes are Car[] and Planes[] respectively
Since many predicates are similar or the same, I am aiming to export them as functions to simplify the code:
const filtered = cars
.filter(byDefined('ownerId'))
.filter(byEq('type', 1))
const filtered = planes
.filter(byEq('ownerId', 1))
// For certain fields, I'd like to shorten this even more to just
const filtered = planes
.filter(byOwnerIdEq(1))
I have created a composeEqual
function that compares any field to a known value:
export const composeEqual =
<
Key extends string,
Value extends unknown,
CheckType extends { [key in Key]: Value },
Type extends CheckType
>(
key: Key,
value: Value
) =>
(obj: Type): obj is Type & CheckType =>
obj[key] === value;
export const byOwnerIdEq = (ownerId: string) => composeEqual('ownerId', ownerId);
However, I am facing an issue when the field's type is a union, causing TypeScript to report an error. Referring to my previous example:
const filtered = cars
.filter(byDefined('ownerId'))
.filter(byEq('type', 1)) // error
I would like the byDefined
function to act as a type guard for cars
in case ownerId
is possibly undefined.
The new type should be:
type N = Omit<Car, 'ownerId'> & { ownerId: number }
Is there a way to achieve this functionality with TypeScript?