Встроенные примеры в документации кода ускоряют понимание и проверку функциональности. @example теги в JSDoc и docstrings в TypeScript позволяют сделать код самодокументирующимся.
/**
* Форматирует дату в относительное время на русском языке.
*
* @param {Date|string|number} date
* @param {string} [locale='ru']
* @returns {string}
*
* @example
* // Прошедшее время
* safeFormat(new Date(Date.now() - 60000)); // "минуту назад"
*
* @example
* // Будущее время
* safeFormat(new Date(Date.now() + 3600000)); // "через час"
*
* @example
* // Null возвращает пустую строку
* safeFormat(null); // ""
*
* @example
* // Другая локаль
* safeFormat(new Date(Date.now() - 60000), 'en_US'); // "1 minute ago"
*/
function safeFormat(date, locale = 'ru') { /* ... */ }
В JavaScript нет встроенного doctest, но существуют библиотеки:
npm install --save-dev jest-docblock
Или вручную проверять через REPL:
// Примеры можно скопировать в browser console и проверить
import { format } from 'timeago.js';
// Пример 1: 5 минут назад
const fiveMinAgo = new Date(Date.now() - 5 * 60000);
console.assert(format(fiveMinAgo, 'ru').includes('минут'), 'Должно содержать "минут"');
## Использование
### Базовый format
\`\`\`js
import { format } from 'timeago.js';
// Прошедшее время
format(new Date(Date.now() - 60_000), 'ru'); // "минуту назад"
format(new Date(Date.now() - 3600_000), 'ru'); // "час назад"
format(new Date(Date.now() - 86400_000), 'ru'); // "вчера"
// Будущее время
format(new Date(Date.now() + 3600_000), 'ru'); // "через час"
\`\`\`
### Автообновление DOM
\`\`\`html
<time datetime="2025-06-01T12:00:00Z">загружается...</time>
\`\`\`
\`\`\`js
import { render, cancel } from 'timeago.js';
const el = document.querySelector('time');
render(el, 'ru');
// При размонтировании
cancel(el);
\`\`\`
TypeScript позволяет включать примеры в declaration файлы:
/**
* Обёртка с полной валидацией.
*
* @example
* ```ts
* const result = tryFormat('2025-06-01T12:00:00Z', 'ru');
* if (result.ok) {
* console.log(result.value); // "год назад"
* } else {
* console.error(result.error);
* }
* ```
*/
export function tryFormat(date: unknown, locale?: string): Result<string, string>;
/**
* Регистрирует сокращённую русскую локаль.
*
* @example
* ```js
* registerShortRu();
* format(new Date(Date.now() - 60_000), 'ru_short'); // "1 мин назад"
* format(new Date(Date.now() - 3600_000), 'ru_short'); // "1 ч назад"
* format(new Date(Date.now() + 60_000), 'ru_short'); // "через 1 мин"
* ```
*/
function registerShortRu(): void {
register('ru_short', (n, i) => {
// ...
});
}
/**
* Хук для отображения относительного времени с автообновлением.
*
* @param date - Дата для форматирования.
* @param locale - Код локали.
* @returns Строка относительного времени.
*
* @example
* ```tsx
* function PostHeader({ post }: { post: Post }) {
* const timeAgo = useTimeAgo(post.createdAt, 'ru');
*
* return (
* <header>
* <h1>{post.title}</h1>
* <time>{timeAgo}</time>
* </header>
* );
* }
* ```
*
* @example Обновление при смене локали
* ```tsx
* const [locale, setLocale] = useState('ru');
* const timeAgo = useTimeAgo(post.createdAt, locale);
* ```
*/
export function useTimeAgo(date: Date | string, locale?: string): string;
// TimeAgo.stories.tsx
import { TimeAgo } from './TimeAgo';
export default {
title: 'Components/TimeAgo',
component: TimeAgo,
};
export const JustNow = {
args: { date: new Date() },
};
export const FiveMinutesAgo = {
args: { date: new Date(Date.now() - 5 * 60_000) },
};
export const OneHour Ago = {
args: { date: new Date(Date.now() - 3600_000) },
};
export const EnglishLocale = {
args: { date: new Date(Date.now() - 3600_000), locale: 'en_US' },
};
// Примеры из документации превращаются в тесты
const EXAMPLES = [
{ input: new Date(Date.now() - 30_000), pattern: /секунд|только что/ },
{ input: new Date(Date.now() - 60_000), pattern: /минуту/ },
{ input: new Date(Date.now() - 3600_000),pattern: /час/ },
];
describe('примеры из документации', () => {
jest.useFakeTimers();
jest.setSystemTime(new Date('2025-06-01T12:00:00Z').getTime());
EXAMPLES.forEach(({ input, pattern }, i) => {
it(`пример ${i + 1} соответствует паттерну`, () => {
expect(format(input, 'ru')).toMatch(pattern);
});
});
});