Подключение в TypeScript проектах

Установка и базовая интеграция

Библиотека устанавливается стандартным способом через менеджеры пакетов:

npm install timeago.js

или

yarn add timeago.js

В современных TypeScript-проектах пакет уже содержит встроенные типы, поэтому дополнительная установка @types/* чаще всего не требуется. Это важный момент: начиная с актуальных версий, типизация распространяется вместе с самой библиотекой, что упрощает интеграцию и снижает вероятность конфликтов версий.


Импорт в TypeScript

Поддерживаются несколько вариантов импорта в зависимости от конфигурации проекта.

ESM (рекомендуемый вариант)
import { format } from 'timeago.js';

const result: string = format(Date.now() - 60000);
Полный импорт модуля
import * as timeago from 'timeago.js';

const result = timeago.format(new Date());
CommonJS-совместимый импорт
const timeago = require('timeago.js');

console.log(timeago.format(new Date()));

При использовании TypeScript в строгом режиме предпочтение обычно отдается ESM-варианту, так как он обеспечивает лучшую статическую проверку типов и tree-shaking в сборщиках.


Типизация основных функций

Ключевая функция библиотеки — format. В TypeScript она имеет строго определённую сигнатуру:

format(input: string | number | Date, locale?: string, options?: object): string

Основные особенности типизации:

  • input принимает:

    • Date — объект даты
    • number — timestamp в миллисекундах
    • string — строковое представление даты
  • Возвращаемое значение всегда string

  • locale опционален и используется для локализации вывода

Пример с явной типизацией:

import { format } from 'timeago.js';

const createdAt: Date = new Date('2024-01-01');

const timeLabel: string = format(createdAt);

Работа с TypeScript strict mode

При включённом strict: true в tsconfig.json библиотека корректно работает без дополнительных настроек:

{
  "compilerOptions": {
    "strict": true,
    "moduleResolution": "node",
    "esModuleInterop": true
  }
}

Особое внимание стоит уделить флагу esModuleInterop, который влияет на корректность импорта CommonJS-модулей.


Локализация и типизация локалей

Библиотека поддерживает локали через регистрацию языков. В TypeScript это оформляется через расширение типов и регистрацию функции локали.

Пример регистрации локали:

import { register } from 'timeago.js';

register('ru', (number: number, index: number): [string, string] => {
  return [
    ['только что', 'через %s секунд'],
    ['1 минуту назад', 'через 1 минуту'],
    ['%s минут назад', 'через %s минут'],
    ['1 час назад', 'через 1 час'],
    ['%s часов назад', 'через %s часов'],
    ['1 день назад', 'через 1 день'],
    ['%s дней назад', 'через %s дней'],
  ][index];
});

Тип возвращаемого значения функции локали строго фиксирован:

(number: number, index: number) => [string, string]

Это важно для предотвращения ошибок в структуре массива переводов.


Использование локали в форматировании

import { format } from 'timeago.js';

const date = new Date();

const result = format(date, 'ru');

TypeScript гарантирует, что второй аргумент соответствует зарегистрированным локалям (в зависимости от конфигурации проекта может быть расширен вручную через declaration merging).


Расширение типов и declaration merging

При необходимости добавления собственных локалей в больших проектах используется расширение типов.

Пример декларации:

declare module 'timeago.js' {
  interface Locales {
    ru: string;
    en: string;
    custom: string;
  }
}

Такой подход позволяет:

  • централизованно управлять доступными локалями
  • предотвращать передачу несуществующих значений
  • улучшать автодополнение в IDE

Использование в React с TypeScript

В React-проектах библиотека часто используется для отображения “времени назад”.

import { useEffect, useState } from 'react';
import { format } from 'timeago.js';

interface Props {
  date: Date;
}

export function TimeLabel({ date }: Props) {
  const [value, setValue] = useState<string>(() => format(date));

  useEffect(() => {
    const id = setInterval(() => {
      setValue(format(date));
    }, 10000);

    return () => clearInterval(id);
  }, [date]);

  return <span>{value}</span>;
}

TypeScript обеспечивает:

  • строгую типизацию пропсов
  • контроль возвращаемого состояния
  • безопасность работы с датами

Интеграция с сборщиками (Vite, Webpack, Rollup)

В современных сборках дополнительных настроек обычно не требуется.

Vite
import { format } from 'timeago.js';

Vite автоматически обрабатывает ESM и TypeScript-типы.

Webpack

При использовании старых конфигураций важно проверить:

  • resolve.extensions включает .ts
  • включен ts-loader или babel-loader с TypeScript preset
Rollup

Рекомендуется использовать @rollup/plugin-typescript для корректной обработки типов.


Частые типовые проблемы

Несовместимость импорта

Ошибка возникает при смешении ESM и CommonJS:

// ошибка при неправильной конфигурации
import timeago from 'timeago.js';

Решение — использовать:

import * as timeago from 'timeago.js';

или включить esModuleInterop.


Неверный тип даты
format('2024-01-01'); // string допустим, но может приводить к неоднозначности

Строгий вариант:

format(new Date('2024-01-01'));

Потеря типов локалей

При динамической регистрации локалей TypeScript не всегда может вывести строгий union-тип. В таких случаях используется явное расширение интерфейсов.


Организация типизированных утилит

В крупных TypeScript-проектах часто создают обёртки над библиотекой:

import { format } from 'timeago.js';

export function formatTimeAgo(date: Date): string {
  return format(date, 'ru');
}

Такой подход:

  • фиксирует локаль на уровне приложения
  • упрощает рефакторинг
  • уменьшает количество ошибок типов

Использование с API-данными

При работе с серверными данными важно учитывать типизацию DTO:

interface PostDTO {
  id: number;
  createdAt: string;
}

function mapPost(post: PostDTO) {
  return {
    ...post,
    createdAtLabel: format(new Date(post.createdAt))
  };
}

TypeScript обеспечивает контроль преобразования строковой даты в объект Date, что критично для корректной работы форматирования времени.