JavaScript: Аннотации типов
В JavaScript в функцию можно передать любые значения. Иногда это усложняет понимание кода: не всегда ясно, что именно ожидает функция и что она возвращает. Чтобы сделать код понятнее, типы описывают явно. В самом синтаксисе JavaScript аннотаций типов нет, но есть стандарт де-факто — JSDoc: специальные комментарии перед функцией, которые понимают редакторы и проверяющие инструменты. Таким образом мы решаем сразу несколько задач:
- Улучшаем работу редактора кода: получаем подсказки, лучше автодополнение и тому подобное.
- Помогаем ИИ-агентам быстрее видеть структуру и принимать более правильные решения, минимизируя случайные ошибки.
- Появляется возможность проверять корректность программы без её запуска, за счёт статической проверки. Такая проверка не гарантирует, что логика программы написана правильно, но, по крайней мере, в ней не будет ошибок типов.
Как указывать типы параметров
JSDoc-комментарий начинается с /** и располагается прямо перед определением функции. Тип каждого параметра указывается тегом @param, тип возвращаемого значения — тегом @returns. Сам тип записывается в фигурных скобках.
Разберём на примере функции, которая вычисляет сумму двух переданных значений:
/**
* @param {number} a
* @param {number} b
* @returns {number}
*/
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // => 5/**
* @param {number} a ← тип параметра a
* @param {number} b ← тип параметра b
* @returns {number} ← тип возвращаемого значения
*/Теперь редактор кода будет подсказывать, что функция add() принимает два числа и возвращает число. Если попытаться передать строку, редактор подсветит это как проблему и предупредит:
add('2', 3); // Argument of type 'string' is not assignable to parameter of type 'number'Какие типы используются в аннотациях
На этом этапе достаточно знать аннотации для простых, примитивных типов данных:
numberдля чисел — в JavaScript один числовой тип и для целых, и для дробныхstringдля строкbooleanдля логических значений (trueилиfalse)
/**
* @param {string} name
* @param {number} age
* @param {number} height
* @returns {string}
*/
function describe(name, age, height) {
return `${name}, ${age} лет, рост ${height}`;
}
console.log(describe('Anna', 25, 1.7));
// => Anna, 25 лет, рост 1.7Если функция ничего не возвращает, в качестве возвращаемого типа указывается void. Например, функция может только печатать текст на экран:
/**
* @param {string} name
* @returns {void}
*/
function printGreeting(name) {
console.log(`Hello, ${name}!`);
}
printGreeting('Anna');
// => Hello, Anna!Пример с параметрами по умолчанию
Аннотации работают одинаково как для обязательных параметров, так и для тех, у которых есть значение по умолчанию. Имя необязательного параметра заключается в квадратные скобки, а после знака = указывается стандартное значение:
/**
* @param {string} name
* @param {string} [greeting='Hello']
* @returns {string}
*/
function greet(name, greeting = 'Hello') {
return `${greeting}, ${name}`;
}
console.log(greet('Anna')); // => Hello, Anna
console.log(greet('Kirill', 'Hi')); // => Hi, KirillВ этом примере name является обязательным параметром, а greeting имеет значение по умолчанию. Аннотации показывают типы обоих параметров и возвращаемого результата.
Аннотации и проверка кода
Сам JavaScript не проверяет JSDoc-аннотации во время выполнения программы, но есть инструменты, которые умеют это делать без запуска кода. Такой подход называют статической проверкой кода.
«Статическая» значит, что проверка происходит ещё до запуска программы. Инструмент читает исходный код и сверяет, соответствуют ли переданные значения указанным типам. Например, если функция принимает строку, а вы передадите число, статическая проверка покажет это как ошибку. В мире JavaScript такую проверку выполняет компилятор TypeScript: в специальном режиме он читает обычные JS-файлы и понимает типы из JSDoc-комментариев.
Особенно удобно, когда такие ошибки подсвечивает редактор прямо во время написания кода. Это позволяет сразу увидеть проблему и исправить её, не дожидаясь запуска программы. Благодаря этому многие неожиданные ошибки отлавливаются заранее, и в работающем коде их становится меньше.
Аннотации не являются обязательными. Функции можно писать и без них, JavaScript всё равно будет работать. Но когда аннотации есть, код становится понятнее для людей и удобнее для редакторов. Аннотирование функций в своём коде считается хорошей практикой. А когда типов становится много, разработчики часто переходят на TypeScript — язык-надмножество JavaScript, где аннотации встроены прямо в синтаксис.
Задание
Приложение создаёт текстовые разделители из повторяющихся символов — например, ------- или =====. Реализуйте функцию wordMultiply(). Она должна принимать два параметра:
- Строку
- Число, которое обозначает, сколько раз нужно повторить строку
И возвращает строку, которая повторяется n раз. Если передаётся ноль, то возвращается пустая строка.
const text = 'javascript';
console.log(wordMultiply(text, 2)); // => javascriptjavascript
console.log(wordMultiply(text, 0)); // =>Укажите JSDoc-аннотации типов при объявлении функции: теги @param для обоих параметров и @returns для возвращаемого значения.
Подсказки
- Для повторения строки удобно использовать метод repeat()
- Не забудьте, что аннотацию типа нужно указать и у возвращаемого значения
Полезное
Ваше упражнение проверяется по этим тестам
// @ts-nocheck -- tsconfig has no Node.js types (types: []), and the test reads the source via node:fs
import { readFileSync } from 'node:fs';
import { expect, test } from 'vitest';
import f from './index.js';
test('type annotations', () => {
const source = readFileSync(new URL('./index.js', import.meta.url), 'utf8');
const paramAnnotations = source.match(/@param\s*\{/g) ?? [];
expect(
paramAnnotations.length,
'Add JSDoc @param annotations for both parameters',
).toBeGreaterThanOrEqual(2);
expect(
source,
'Add a JSDoc @returns annotation for the return value',
).toMatch(/@returns\s*\{/);
expect(f('javascript', 1)).toBe('javascript');
expect(f('javascript', 3)).toBe('javascriptjavascriptjavascript');
expect(f('java', 0)).toBe('');
});Решение учителя откроется через:
20:00
