Reputation: 474
Transferred js typescript, faced with the problem. The following function works with two types of data, I see this error:
Property 'dateTime' does not exist on type 'Operation | OperationCreated'.
Property 'dateTime' does not exist on type 'OperationCreated'
type DateTime = {
date: string;
};
type Operation = {
dateTime: DateTime;
};
type OperationCreated = {
createdDate: string;
};
const sortByDate = (o1: Operation | OperationCreated, o2: Operation | OperationCreated) =>
stringToMillisecond(o1.createdDate || o1.dateTime.date) - stringToMillisecond(o2.createdDate || o2.dateTime.date);
Making the first steps in typescript, please help
Upvotes: 2
Views: 3070
Reputation: 1014
This error is because the compiler doesn't know if o1
and o2
are Operation
or OperationCreated
. You may use discriminated unions.
My solution (to clean)
interface DateTime {
date: string;
}
interface OperationDefault {
type: 'default';
dateTime: DateTime;
}
interface OperationCreated {
type: 'created';
createdDate: string;
}
type Operation = OperationDefault | OperationCreated;
const sortByDate = (o1: Operation, o2: Operation) => {
const d1 = o1.type === 'default' ? o1.dateTime.date : o1.createdDate;
const d2 = o2.type === 'default' ? o2.dateTime.date : o2.createdDate;
return stringToMillisecond(d1) - stringToMillisecond(d2);
};
Upvotes: 1
Reputation: 330171
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
Upvotes: 5
Reputation: 11235
Use an interface instead of a type literal
interface DateTime {
date: string;
}
interface Operation {
dateTime: DateTime;
}
interface OperationCreated {
createdDate: string;
}
const sortByDate = (
o1: Operation | OperationCreated,
o2: Operation | OperationCreated
) =>
stringToMillisecond((o1 as any).createdDate || (o1 as any).dateTime.date) -
stringToMillisecond((o2 as any).createdDate || (o2 as any).dateTime.date);
Upvotes: -1