TypeScript does not perceive o1.createdDate || o1.dateTime.date as valid for a few reasons:
The biggest one in terms of runtime impact is that you presume that o1.createdDate will only be falsy if it is undefined. But the empty string is also falsy. If someone calls this:
sortByDate({ createdDate: "" }, { createdDate: "" });
then you will end up calling stringToMillisecond(undefined), which I would suspect is an error. So I would give up on using a falsy check on possibly-string values here.
The TypeScript compiler does not let you read a property value of a union type unless every member of the union contains a (possibly optional) property with that key. So o1.createdDate is an error unless you can narrow o1 to a OperationCreated first, by differentiating between the possible values. One way to do this is to use the in operator, like:
"createdDate" in o1 ? o1.createdDate : o1.dateTime.date; // no error
This works because the compiler uses the in check to narrow o1 to OperationCreated in the "then" clause of the ternary operator, or to OperationDefault in the "else" clause.
So the most straightforward fix I could imagine here is:
const sortByDate = (
o1: Operation | OperationCreated,
o2: Operation | OperationCreated
) =>
stringToMillisecond("createdDate" in o1 ? o1.createdDate : o1.dateTime.date) -
stringToMillisecond("createdDate" in o2 ? o2.createdDate : o2.dateTime.date);
Okay, hope that helps. Good luck! Link to code