Moment(). add() работает только с литеральными значениями

Я использую Moment.js в TypeScript (при Angular 2, если это имеет значение). Когда я использую метод add() с литеральными значениями в качестве аргументов, он отлично работает:

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

Однако, если я попытаюсь заменить единицы на строку, это не сработает:

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

с этой ошибкой:

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

Что я здесь делаю неправильно?

Ответ 1

Устаревшее обратное add(unit: unitOfTime.DurationConstructor, amount: number|string) перегрузки add(unit: unitOfTime.DurationConstructor, amount: number|string) создает неоднозначность.

Вы можете исправить это, определив тип units как DurationConstructor вместо string:

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

Другой вариант - просто использовать const вместо let, поэтому будет выведен буквенный тип:

const units = 'month';
moment().add(1, units);

Ответ 2

В моем случае проблема заключалась в том, что я использовал mins. Тип def для DurationConstructor -

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"
);

Итак, это получается из коробки:

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

-k

Ответ 3

Другой вариант принятого ответа - приведение типа в аргументе. Там действительно нет никакой разницы, просто решил, что я бы включил этот ответ в качестве опции. Кроме того, unitOfTime может быть импортирован мгновенно как модуль, если вы хотите немного краткости.

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

option = {val: 30, unit: 'm'}
moment().add( this.querySince.val, <unitOfTime.DurationConstructor>this.querySince.unit )

Ответ 4

Есть два объявления о add

  1. add(amount?: DurationInputArg1, unit?: DurationInputArg2): Moment;
  2. Устаревшее add(unit: unitOfTime.DurationConstructor, amount: number|string)

Когда машинописный текст должен соответствовать перегрузкам, он пытается принять наиболее близкий тип. Когда вы попытались передать units:string, номер ближайшего совпадения number|string является второй перегрузкой, потому что любая строка будет соответствовать этому объявлению, например, 'qwe' или 'rty' типа string, но не DurationArg2 который ожидается как второй параметр в первом объявлении.

Когда вы вызываете moment.add(1,'months) он будет использовать первое объявление, потому что можно привести 1 к типу первого аргумента в первой сигнатуре, но не во второй.

Таким образом, чтобы исправить эту проблему и подобное, вы должны сказать, что именно вы хотите использовать.

Пример 1. Будет использована первая подпись

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()
    }
  }

Пример 2. Будет использоваться вторая подпись

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()
    }
  }

Ответ 5

Эта проблема вызвана устаревшим обратным синтаксисом функций add, subtract и etc в библиотеке минут. Ниже приведен фрагмент:

moment.d.ts

add(amount?: DurationInputArg1, unit?: DurationInputArg2): Moment;
/**
 * @deprecated reverse syntax
 */
add(unit: unitOfTime.DurationConstructor, amount: number|string): Moment;

В моем случае я хотел, чтобы единицы и значение оба были переменными, поэтому мне пришлось написать функцию для преобразования строки general в приемлемые ( "часы", "минуты" и т.д.).

function convertToDuration (unit: string): Moment.unitOfTime.DurationConstructor {

if(unit ==  'seconds' || unit ==  'minutes' || unit ==  'hours' || unit ==  'days' || unit ==  'weeks' || unit ==  'months'){
    return unit;
}
else // Default unit is hours
    return 'hours';
}

Теперь для вызова этой функции:

const time = moment().add(expiryTime.value, convertToDuration(expiryTime.unit));

Определение expiryTime в TypeScript:

interface ExpiryTimeDef {
value: number,
unit: string
}

Ответ 6

Измените определение типа с let на const

const units = 'month';
moment().add(1, units);

это работает для меня..

Ответ 7

Этот ответ основан на всех предыдущих ответах, deprecated reverse syntax of add, subtract, etc functions in moment в качестве причины проблемы указан deprecated reverse syntax of add, subtract, etc functions in moment. Д.

Я unitOfTime.Base поле DurationInputArg2 к unitOfTime.Base так как все эти методы имеют этот конечный суперкласс, и это означает, что для add, subtract, startOf, endOf мне просто нужно помнить as Base:

 import Base = moment.unitOfTime.Base

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

Запутывающая часть для большинства людей в том, что amount помечена как ошибка. Исправлено, когда один квалифицирует юнитов