Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

moment().add() only works with literal values

I'm using Moment.js in TypeScript (under Angular 2 if that matters). When I use the add() method with literal values as arguments, it works fine:

moment().add(1, 'month');

However, if I try to replace the units with a string, it fails:

let units:string = 'month';
moment().add(1, units);

with this error:

Argument of type '1' is not assignable to parameter of type 'DurationConstructor'.

What am I doing wrong here?

like image 435
rocklobster Avatar asked Jan 20 '17 17:01

rocklobster


People also ask

How do you add moments to date and time?

The add function is used to add date and time to the moment object and the subtract function subtract date and time from the moment object. const moment = require('moment'); let now = moment(); console.

How do you add 1 month in moment?

Try moment('2020-03-30'). add(1, 'months') and then compare with moment('2020-03-31'). add(1, 'months') .

How do you add hours to a moment object?

To add hours to a parsed moment date with JavaScript, we can use the add method. to add 5 hours to the moment object parsed from myDate . We call add with 5 and 'hours' to add 5 hours. And then we call format with a format string to return the datetime stored in the moment object.

Is moment mutable in moment JS?

The moment object in Moment.js is mutable. This means that operations like add, subtract, or set change the original moment object. As you can see, adding one week mutated a.

Can I add or subtract decimals in moment's API?

Because of the variability of duration in day math, Moment's API does not officially support adding or subtracting decimal values for days and larger. Moment.js will accept decimal values and do its best to handle them by rounding to the nearest whole number.

How do I print the value of a moment object?

To print out the value of a Moment, use .format (), .toString () or .toISOString (). To retrieve a native Date object from Moment, use .toDate (). This function returns a properly shifted date for interaction with third party APIs. Moment.js has a very flexible and advanced parser that allows a huge range of functionality.

How to define a type that represents an enumeration of literal values?

Using literal types with union types (used to compose types) you could define a type that represents an enumeration of literal values. For example, we can define the type Weather.


6 Answers

Deprecated reverse overload add(unit: unitOfTime.DurationConstructor, amount: number|string) creates ambiguity.

You can fix this by defining type of units to be DurationConstructor instead of string:

let units: moment.unitOfTime.DurationConstructor = 'month';
moment().add(1, units);

Another option is just to use const instead of let, so literal type will be inferred:

const units = 'month';
moment().add(1, units);
like image 126
Aleksey L. Avatar answered Oct 01 '22 06:10

Aleksey L.


Another option to the accepted answer is typecasting in the argument. There's really no difference, just figured I'd include this answer as an option. Also unitOfTime can be imported from moment as a module if you want some more brevity.

import { unitOfTime } from 'moment';
import * as moment from 'moment';

option = {val: 30, unit: 'm'}
moment().add( this.querySince.val, <unitOfTime.DurationConstructor>this.querySince.unit )
like image 23
Alex Spera Avatar answered Oct 01 '22 06:10

Alex Spera


Unfortunately, none of the above answers worked with me! But this only made the charm! :D

const startTime = moment().subtract(this.time.amount as moment.DurationInputArg1, this.time.unit as moment.DurationInputArg2);
like image 40
Ammar Ismaeel Avatar answered Oct 01 '22 06:10

Ammar Ismaeel


There are two declarations of add

  1. add(amount?: DurationInputArg1, unit?: DurationInputArg2): Moment;
  2. Deprecated add(unit: unitOfTime.DurationConstructor, amount: number|string)

When typescript need to match overloads it tries to take closest type. When you've tried to pass units:string, closest match number|string is second overload, because any string will match to this declaration, f.e. 'qwe', or 'rty' of type string, but not DurationArg2 which is expected as second parameter in first declaration.

When you call moment.add(1,'months) it will use first declaration, because it is possible to cast 1 to type of the first argument in first signature, but not in the second.

So to fix this issue and similar, you should say what you exactly want to use.

Example 1. First signature will be used

import * as moment from 'moment'

calculateRangeFromEnd(end: Date, unitsAmount: moment.DurationInputArg1, unitsMeasureMoment: moment.DurationInputArg2): IDateRange {
    return {
      endDate: end,
      startDate: moment(end).subtract(unitsAmount, unitsMeasureMoment).toDate()
    }
  }

Example 2. Second signature will be used

import * as moment from 'moment'

calculateRangeFromEnd(end: Date, unitsAmount: number | string, unitsMeasureMoment: moment.unitOfTimes.DurationConstructor): IDateRange {
    return {
      endDate: end,
      startDate: moment(end).subtract(unitsMeasureMoment, unitsAmount).toDate()
    }
  }
like image 28
Nikita Avatar answered Oct 01 '22 05:10

Nikita


The problem in my case was that I was using mins. The type def for DurationConstructor is

namespace unitOfTime {
type Base = (
  "year" | "years" | "y" |
  "month" | "months" | "M" |
  "week" | "weeks" | "w" |
  "day" | "days" | "d" |
  "hour" | "hours" | "h" |
  "minute" | "minutes" | "m" |
  "second" | "seconds" | "s" |
  "millisecond" | "milliseconds" | "ms"
);

So this works out of the box:

refTime.clone().subtract(15, 'minute')

-k

like image 35
kontinuity Avatar answered Oct 01 '22 05:10

kontinuity


This answer is building on all the previous answers that specify deprecated reverse syntax of add, subtract, etc functions in moment as the cause of the problem.

I cast the DurationInputArg2 field to unitOfTime.Base since all these methods have this as the ultimate superclass and so it means for add, subtract, startOf, endOf I just need to remember as Base:

 import Base = moment.unitOfTime.Base

 theMoment.subtract(amount, unitString as Base)
 theMoment.startOf(unitString as Base)

The confusing part for most people is that the amount is marked as the error. Fixed when one qualifies the units.

like image 28
HankCa Avatar answered Oct 01 '22 05:10

HankCa