I have an event type in Typescript that looks like this:
export type EventRecord = {
name: string;
eta: string | null;
assumed_time: string | null;
indicated_time: string | null;
};
and function that displays the time of that event:
export const displayTime = (event: EventRecord): string | null =>
event.indicated_time || event.assumed_time || event.eta;
What I want to achieve is to make sure that at least one of the three times (eta, assumed_time, indicated_time) is not null, so that when my event is Marked as done I'll be able to display that time on the timeline component. I filter my events with Remeda to first, filter events tot only marked ones, and then displaying them:
const timelineEvents = R.pipe(
events,
R.filter(eventMarked),
R.sortBy(displayTime),
R.reverse
);
{timelineEvents.map((event: EventRecord) => {
const time = new Date(displayTime(event)!);
return (
<TimelineEntry
name={event.name}
time={time}
/>
</RecordContextProvider>
);
})}
Here is my eventMarked function, it basically check if there is at least one time provided:
export const eventMarked = (event: eventRecord): boolean =>
!!(event.assumed_time || event.indicated_time || event.eta);
The problem with this setup is that I keep getting errors:
S2345: Argument of type 'unknown[]' is not assignable to parameter of type '(input: EventRecord[]) => unknown'.
Type 'unknown[]' provides no match for the signature '(input: EventRecord[]): unknown'.
80 | events,
81 | R.filter(eventMarked),
> 82 | R.sortBy(displayTime),
| ^^^^^^^^^^^^^^^^^^^^^
83 | R.reverse
84 | );
TS2769: No overload matches this call.
Overload 1 of 2, '(array: readonly unknown[], ...sorts: SortRule<unknown>[]): unknown[]', gave the following error.
Argument of type '(event: EventRecord) => string | null' is not assignable to parameter of type 'readonly unknown[]'.
Overload 2 of 2, '(sort: SortRule<EventRecord>, ...sorts: SortRule<EventRecord>[]): (array: readonly EventRecord[]) => EventRecord[]', gave the following error.
Argument of type '(event: EventRecord) => string | null' is not assignable to parameter of type 'SortRule<EventRecord>'.
Type '(event: EventRecord) => string | null' is not assignable to type 'SortProjection<EventRecord>'.
Type 'string | null' is not assignable to type 'Comparable'.
Type 'null' is not assignable to type 'Comparable'.
80 | events,
81 | R.filter(eventMarked),
> 82 | R.sortBy(displayTime),
| ^^^^^^^^^^^
83 | R.reverse
84 | );
85 |
TS2339: Property 'map' does not exist on type '(array: readonly unknown[]) => unknown[]'.
114 | />
115 | )}
> 116 | {timelineEvents.map((event: EventRecord) => {
| ^^^
I think the cause of this error might be that typescript doesn't really know what type is EventRecord because display is either null or string. How can this problem be solved?
You can make a few changes in your types to help solve this.
Acknowledging that all of the keys in
EventRecordof typestring | nullare related (and are the ones that need to be checked in the runtime code for a best match), you can define them in an array using aconstassertion and then use that to define your type along with an optionaltimeproperty. Then you can define another type that has thetimeproperty as non-optional.That might look like this:
TS Playground
Then, you can convert your existing filter function to a type guard, which has a predicate as its return type. This will help the compiler understand that if this function returns
true, then the object element definitely has a time property that's aDate. In the version shared in the code below, I've written the function to first check if the expected property exists and is of the correct type. If not, the array from above is used to check each time key and — if one exists, thetimeproperty is set and the function returnstrueearly. If all are falsy, then function returnsfalse.I've also included a standard compare function to use with
Array.prototype.sort()to sort by time (latest first):TS Playground
Putting that together in a reproducible example looks like this, and I've included the compiled JS below for you to run in a code snippet here:
TS Playground
Compiled JS snippet:
When used with a React component, you shouldn't have any trouble:
TS Playground
As far as the Ramda functional library is concerned: you don't show what
eventsis in your question, so I can't reproduce your example. (Are you sure you're usingpipecorrectly?) At any rate, Ramda isn't strictly needed for this context, but you can adapt the TS techniques above to the Ramda functions if desired.