Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Working around unset "length" property on partial functions created with lodash's partialRight

I'm working with MomentTimezone for time manipulation in the browser.

I am using TypeScript and Lodash too.

I have some accountTimezone set on the window containing the authenticated user's preferred timezone. I am trying to create a helper method localMoment() that will accept any of the many signatures of moment.tz(), appending this window.accountTimezone as the final timezone: string argument.

It seems partialRight may be what I'm looking for.

const localMoment = partialRight(moment.tz, window.accountTimezone);

The problem I'm having has to do with this note from the lodash docs:

Note: This method doesn't set the "length" property of partially applied functions.

Specifically, for a call like localMoment('2019-08-01 12:00:00'), TypeScript complains that localMoment() was provided 1 argument, but expects zero.

How can I keep TypeScript happily understanding a call to localMoment() should look like a call to moment.tz() via the MomentTimzone interface, while avoiding this arity confusion from the use of partialRight()?


I considered something like this as an alternative, but don't know how to type ...args to keep TypeScript happy.

const localMoment = (...args): Moment => moment.tz(...args, window.accountTimezone);
like image 202
deefour Avatar asked Aug 16 '19 21:08

deefour


1 Answers

There is no clean way to do this. You either have to opt out of typing or redeclare an interface of your own.

Typescript himself can't do this and just opt for the "good-enough" solution of declaring a bunch of different signatures: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/lodash/common/function.d.ts#L1147

Even if you somehow manage to manipulate the typescript interface, I doubt you could handle the other methods (zone, add, link, ...) which don't have a timezone parameter: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/moment-timezone/moment-timezone.d.ts#L20

The best you can achieve is avoiding duplicating the whole interface using the Pick utility type:

type CurriedMomentTimezone = Pick<moment.MomentTimezone, 'zone' | 'add' | 'link' | 'load' | 'names' | 'guess' | 'setDefault'> & {
    (): moment.Moment;
    (date: number): moment.Moment;
    (date: number[]): moment.Moment;
    (date: string): moment.Moment;
    (date: string, format: moment.MomentFormatSpecification): moment.Moment;
    (date: string, format: moment.MomentFormatSpecification, strict: boolean): moment.Moment;
    (date: string, format: moment.MomentFormatSpecification, language: string): moment.Moment;
    (date: string, format: moment.MomentFormatSpecification, language: string, strict: boolean): moment.Moment;
    (date: Date): moment.Moment;
    (date: moment.Moment): moment.Moment;
    (date: any): moment.Moment;
}

localMoment = _.partialRight(moment.tz, this.accountTimezone) as CurriedMomentTimezone;
like image 98
Nicolas Reynis Avatar answered Oct 09 '22 16:10

Nicolas Reynis