I think the main problem in your original compareByClosestTo()
function is that there is no good inference site for T
. The key
parameter is of type keyof T
. If you pass in, say, "foo"
for key
, you'd need the compiler to infer a type T
for which "foo"
is one of its keys. The compiler might infer {foo: DateTime}
, depending on how you use it, or it might give up and infer something like {foo: any}
or even {[k: string]: any}
. The only thing you can expect the compiler to reliably infer from a value of type "foo"
, is the string "foo"
itself.
To that end, let's dispense with T extends Dateable<T>
and instead look at K extends PropertyKey
. Then we can express T
in terms of K
: in this case, { [P in K]: DateTime }
, which is equivalent to Record<K, DateTime>
(using the Record
utility type):
const compareByClosestTo = <K extends PropertyKey>(key: K, date: DateTime) =>
(l: { [P in K]: DateTime }, r: { [P in K]: DateTime }) =>
Math.abs(l[key].diff(date).milliseconds) - Math.abs(r[key].diff(date).milliseconds);
Let's see what happens when we call it:
const compareFn = compareByClosestTo("recieved", DateTime.local());
/* const compareFn: (l: {
recieved: DateTime;
}, r: {
recieved: DateTime;
}) => number */
Here, K
is inferred to be "received"
, and the resulting compare function is the {received: DateTime}
type we want. And so both of the following sort()
calls work:
const works = dates.map(dt => ({ recieved: dt }))
.sort(compareByClosestTo("recieved", DateTime.local()))
const stillWorks = dates.map((dt, i) => ({ recieved: dt, index: i }))
.sort(compareByClosestTo("recieved", DateTime.local()))
because each of the arrays you're sorting have elements assignable to {received: DateTime}
.
Playground link to code
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…