From 6b70d6035dc76c1d377544109803217fba5fb125 Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Wed, 10 Jun 2026 21:57:04 +0500 Subject: [PATCH] feat(40-define-functions): add 350-type-annotations lesson (JSDoc), port of Python 300 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Аналог питоновского 300-type-annotations: зачем нужны аннотации, синтаксис @param/@returns, примитивные типы, void, параметры по умолчанию, статическая проверка (tsc checkJs). Упражнение wordMultiply — порт питоновского; тест требует JSDoc-аннотации (чтение исходника) + проверяет поведение. Co-Authored-By: Claude Fable 5 --- .../350-type-annotations/Makefile | 2 + .../350-type-annotations/description.es.yml | 72 ++++++++++++ .../350-type-annotations/en/EXERCISE.md | 20 ++++ .../350-type-annotations/en/README.md | 35 ++++++ .../350-type-annotations/en/data.yml | 14 +++ .../350-type-annotations/es/EXERCISE.md | 20 ++++ .../350-type-annotations/es/README.md | 35 ++++++ .../350-type-annotations/es/data.yml | 14 +++ .../350-type-annotations/index.js | 12 ++ .../350-type-annotations/ru/EXERCISE.md | 20 ++++ .../350-type-annotations/ru/README.md | 107 ++++++++++++++++++ .../350-type-annotations/ru/data.yml | 14 +++ .../350-type-annotations/test.js | 22 ++++ 13 files changed, 387 insertions(+) create mode 100644 modules/40-define-functions/350-type-annotations/Makefile create mode 100644 modules/40-define-functions/350-type-annotations/description.es.yml create mode 100644 modules/40-define-functions/350-type-annotations/en/EXERCISE.md create mode 100644 modules/40-define-functions/350-type-annotations/en/README.md create mode 100644 modules/40-define-functions/350-type-annotations/en/data.yml create mode 100644 modules/40-define-functions/350-type-annotations/es/EXERCISE.md create mode 100644 modules/40-define-functions/350-type-annotations/es/README.md create mode 100644 modules/40-define-functions/350-type-annotations/es/data.yml create mode 100644 modules/40-define-functions/350-type-annotations/index.js create mode 100644 modules/40-define-functions/350-type-annotations/ru/EXERCISE.md create mode 100644 modules/40-define-functions/350-type-annotations/ru/README.md create mode 100644 modules/40-define-functions/350-type-annotations/ru/data.yml create mode 100644 modules/40-define-functions/350-type-annotations/test.js diff --git a/modules/40-define-functions/350-type-annotations/Makefile b/modules/40-define-functions/350-type-annotations/Makefile new file mode 100644 index 00000000..d0d0a48c --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/Makefile @@ -0,0 +1,2 @@ +test: + @ test.sh diff --git a/modules/40-define-functions/350-type-annotations/description.es.yml b/modules/40-define-functions/350-type-annotations/description.es.yml new file mode 100644 index 00000000..fecea709 --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/description.es.yml @@ -0,0 +1,72 @@ +--- + +name: Anotaciones de tipos +theory: | + En JavaScript se puede pasar cualquier valor a una función. A veces esto complica la comprensión del código: no siempre queda claro qué espera la función y qué devuelve. La sintaxis de JavaScript no tiene anotaciones de tipos, pero existe un estándar de facto — **JSDoc**: comentarios especiales antes de la función que entienden los editores y las herramientas de verificación. + + ## Cómo anotar los tipos de los parámetros + + Un comentario JSDoc comienza con `/**` y se coloca justo antes de la definición de la función. El tipo de cada parámetro se declara con la etiqueta `@param`, y el tipo del valor de retorno con `@returns`. El tipo se escribe entre llaves: + + ```javascript + /** + * @param {number} a + * @param {number} b + * @returns {number} + */ + function add(a, b) { + return a + b; + } + + console.log(add(2, 3)); // => 5 + ``` + + Ahora el editor sabe que `add()` recibe dos números y devuelve un número. Si intentas pasar una cadena, el editor lo marcará como un problema. + + ## Qué tipos se usan + + En esta etapa basta con conocer las anotaciones de los tipos primitivos: + + - `number` para números — JavaScript tiene un único tipo numérico para enteros y decimales + - `string` para cadenas + - `boolean` para valores lógicos (`true` o `false`) + + Si una función no devuelve nada, se usa `void` como tipo de retorno. Para parámetros opcionales con valores por defecto, el nombre del parámetro se encierra entre corchetes: `@param {string} [greeting='Hello']`. + + ## Anotaciones y verificación estática + + JavaScript no verifica las anotaciones JSDoc durante la ejecución, pero hay herramientas que pueden hacerlo sin ejecutar el código — esto se llama **verificación estática**. En el mundo de JavaScript, el compilador de TypeScript puede leer archivos JS y entender los tipos de los comentarios JSDoc. Las anotaciones son opcionales, pero usarlas se considera una buena práctica. Cuando los tipos se vuelven numerosos, los desarrolladores suelen pasar a **TypeScript** — un superconjunto de JavaScript con anotaciones integradas en la sintaxis. + +instructions: | + La aplicación crea separadores de texto a partir de caracteres repetidos — por ejemplo, `-------` o `=====`. Implementa la función `wordMultiply()`. Recibe dos parámetros: + + * Una cadena + * Un número que indica cuántas veces hay que repetir la cadena + + Devuelve la cadena repetida n veces. Si se pasa cero, devuelve una cadena vacía. + + ```javascript + const text = 'javascript'; + console.log(wordMultiply(text, 2)); // => javascriptjavascript + console.log(wordMultiply(text, 0)); // => + ``` + + Añade anotaciones de tipos JSDoc en la definición de la función: etiquetas `@param` para ambos parámetros y `@returns` para el valor de retorno. + + ## Pistas + + * El método de cadenas [repeat()](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/String/repeat) resulta útil aquí + * No olvides la anotación del valor de retorno + +tips: + - > + [JSDoc en el manual de + TypeScript](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) + - > + [Método + repeat()](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/String/repeat) +definitions: + - name: Anotación de tipos + description: declaración explícita de los tipos de los parámetros y del valor de retorno de una función + - name: Verificación estática + description: comprobación de la corrección del código sin ejecutarlo diff --git a/modules/40-define-functions/350-type-annotations/en/EXERCISE.md b/modules/40-define-functions/350-type-annotations/en/EXERCISE.md new file mode 100644 index 00000000..b40f642e --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/en/EXERCISE.md @@ -0,0 +1,20 @@ + +The application builds text separators from repeating characters — for example, `-------` or `=====`. Implement a `wordMultiply()` function. It takes two parameters: + +* A string +* A number that tells how many times the string should be repeated + +It returns the string repeated n times. If zero is passed, it returns an empty string. + +```javascript +const text = 'javascript'; +console.log(wordMultiply(text, 2)); // => javascriptjavascript +console.log(wordMultiply(text, 0)); // => +``` + +Add JSDoc type annotations to the function definition: `@param` tags for both parameters and `@returns` for the return value. + +## Hints + +* The string method [repeat()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat) is handy here +* Do not forget the annotation for the return value diff --git a/modules/40-define-functions/350-type-annotations/en/README.md b/modules/40-define-functions/350-type-annotations/en/README.md new file mode 100644 index 00000000..a5012e7a --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/en/README.md @@ -0,0 +1,35 @@ + +In JavaScript, you can pass any values to a function. Sometimes this makes the code harder to understand: it is not always clear what the function expects and what it returns. JavaScript syntax itself has no type annotations, but there is a de facto standard — **JSDoc**: special comments placed before a function that editors and checking tools understand. + +## How to annotate parameter types + +A JSDoc comment starts with `/**` and goes right before the function definition. Each parameter type is declared with the `@param` tag, and the return type with the `@returns` tag. The type itself is written in curly braces: + +```javascript +/** + * @param {number} a + * @param {number} b + * @returns {number} + */ +function add(a, b) { + return a + b; +} + +console.log(add(2, 3)); // => 5 +``` + +Now the editor knows that `add()` takes two numbers and returns a number. If you try to pass a string, the editor will highlight it as a problem. + +## Which types are used + +At this point, it is enough to know annotations for primitive types: + +- `number` for numbers — JavaScript has a single numeric type for both integers and floats +- `string` for strings +- `boolean` for logical values (`true` or `false`) + +If a function returns nothing, use `void` as the return type. For optional parameters with default values, the parameter name is wrapped in square brackets: `@param {string} [greeting='Hello']`. + +## Annotations and static checking + +JavaScript itself does not check JSDoc annotations at runtime, but tools can do it without running the code — this is called **static checking**. In the JavaScript world, the TypeScript compiler can read plain JS files and understand types from JSDoc comments. Annotations are optional, but using them is considered good practice. When types grow numerous, developers often switch to **TypeScript** — a superset of JavaScript with annotations built into the syntax. diff --git a/modules/40-define-functions/350-type-annotations/en/data.yml b/modules/40-define-functions/350-type-annotations/en/data.yml new file mode 100644 index 00000000..04b636ed --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/en/data.yml @@ -0,0 +1,14 @@ +--- +name: Type annotations +tips: + - > + [JSDoc in the TypeScript + handbook](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) + - > + [repeat() + method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat) +definitions: + - name: Type annotation + description: an explicit declaration of parameter and return value types of a function + - name: Static checking + description: verifying code correctness without running it diff --git a/modules/40-define-functions/350-type-annotations/es/EXERCISE.md b/modules/40-define-functions/350-type-annotations/es/EXERCISE.md new file mode 100644 index 00000000..23dbc749 --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/es/EXERCISE.md @@ -0,0 +1,20 @@ + +La aplicación crea separadores de texto a partir de caracteres repetidos — por ejemplo, `-------` o `=====`. Implementa la función `wordMultiply()`. Recibe dos parámetros: + +* Una cadena +* Un número que indica cuántas veces hay que repetir la cadena + +Devuelve la cadena repetida n veces. Si se pasa cero, devuelve una cadena vacía. + +```javascript +const text = 'javascript'; +console.log(wordMultiply(text, 2)); // => javascriptjavascript +console.log(wordMultiply(text, 0)); // => +``` + +Añade anotaciones de tipos JSDoc en la definición de la función: etiquetas `@param` para ambos parámetros y `@returns` para el valor de retorno. + +## Pistas + +* El método de cadenas [repeat()](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/String/repeat) resulta útil aquí +* No olvides la anotación del valor de retorno diff --git a/modules/40-define-functions/350-type-annotations/es/README.md b/modules/40-define-functions/350-type-annotations/es/README.md new file mode 100644 index 00000000..c9a5ee9d --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/es/README.md @@ -0,0 +1,35 @@ + +En JavaScript se puede pasar cualquier valor a una función. A veces esto complica la comprensión del código: no siempre queda claro qué espera la función y qué devuelve. La sintaxis de JavaScript no tiene anotaciones de tipos, pero existe un estándar de facto — **JSDoc**: comentarios especiales antes de la función que entienden los editores y las herramientas de verificación. + +## Cómo anotar los tipos de los parámetros + +Un comentario JSDoc comienza con `/**` y se coloca justo antes de la definición de la función. El tipo de cada parámetro se declara con la etiqueta `@param`, y el tipo del valor de retorno con `@returns`. El tipo se escribe entre llaves: + +```javascript +/** + * @param {number} a + * @param {number} b + * @returns {number} + */ +function add(a, b) { + return a + b; +} + +console.log(add(2, 3)); // => 5 +``` + +Ahora el editor sabe que `add()` recibe dos números y devuelve un número. Si intentas pasar una cadena, el editor lo marcará como un problema. + +## Qué tipos se usan + +En esta etapa basta con conocer las anotaciones de los tipos primitivos: + +- `number` para números — JavaScript tiene un único tipo numérico para enteros y decimales +- `string` para cadenas +- `boolean` para valores lógicos (`true` o `false`) + +Si una función no devuelve nada, se usa `void` como tipo de retorno. Para parámetros opcionales con valores por defecto, el nombre del parámetro se encierra entre corchetes: `@param {string} [greeting='Hello']`. + +## Anotaciones y verificación estática + +JavaScript no verifica las anotaciones JSDoc durante la ejecución, pero hay herramientas que pueden hacerlo sin ejecutar el código — esto se llama **verificación estática**. En el mundo de JavaScript, el compilador de TypeScript puede leer archivos JS y entender los tipos de los comentarios JSDoc. Las anotaciones son opcionales, pero usarlas se considera una buena práctica. Cuando los tipos se vuelven numerosos, los desarrolladores suelen pasar a **TypeScript** — un superconjunto de JavaScript con anotaciones integradas en la sintaxis. diff --git a/modules/40-define-functions/350-type-annotations/es/data.yml b/modules/40-define-functions/350-type-annotations/es/data.yml new file mode 100644 index 00000000..87c0c9e2 --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/es/data.yml @@ -0,0 +1,14 @@ +--- +name: Anotaciones de tipos +tips: + - > + [JSDoc en el manual de + TypeScript](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) + - > + [Método + repeat()](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/String/repeat) +definitions: + - name: Anotación de tipos + description: declaración explícita de los tipos de los parámetros y del valor de retorno de una función + - name: Verificación estática + description: comprobación de la corrección del código sin ejecutarlo diff --git a/modules/40-define-functions/350-type-annotations/index.js b/modules/40-define-functions/350-type-annotations/index.js new file mode 100644 index 00000000..a85e808d --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/index.js @@ -0,0 +1,12 @@ +// BEGIN +/** + * @param {string} text + * @param {number} repetitions + * @returns {string} + */ +function wordMultiply(text, repetitions) { + return text.repeat(repetitions); +} +// END + +export default wordMultiply; diff --git a/modules/40-define-functions/350-type-annotations/ru/EXERCISE.md b/modules/40-define-functions/350-type-annotations/ru/EXERCISE.md new file mode 100644 index 00000000..9babff5d --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/ru/EXERCISE.md @@ -0,0 +1,20 @@ + +Приложение создаёт текстовые разделители из повторяющихся символов — например, `-------` или `=====`. Реализуйте функцию `wordMultiply()`. Она должна принимать два параметра: + +* Строку +* Число, которое обозначает, сколько раз нужно повторить строку + +И возвращает строку, которая повторяется n раз. Если передаётся ноль, то возвращается пустая строка. + +```javascript +const text = 'javascript'; +console.log(wordMultiply(text, 2)); // => javascriptjavascript +console.log(wordMultiply(text, 0)); // => +``` + +Укажите JSDoc-аннотации типов при объявлении функции: теги `@param` для обоих параметров и `@returns` для возвращаемого значения. + +## Подсказки + +* Для повторения строки удобно использовать метод [repeat()](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/repeat) +* Не забудьте, что аннотацию типа нужно указать и у возвращаемого значения diff --git a/modules/40-define-functions/350-type-annotations/ru/README.md b/modules/40-define-functions/350-type-annotations/ru/README.md new file mode 100644 index 00000000..0851f668 --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/ru/README.md @@ -0,0 +1,107 @@ + +В JavaScript в функцию можно передать любые значения. Иногда это усложняет понимание кода: не всегда ясно, что именно ожидает функция и что она возвращает. Чтобы сделать код понятнее, типы описывают явно. В самом синтаксисе JavaScript аннотаций типов нет, но есть стандарт де-факто — **JSDoc**: специальные комментарии перед функцией, которые понимают редакторы и проверяющие инструменты. Таким образом мы решаем сразу несколько задач: + +- Улучшаем работу редактора кода: получаем подсказки, лучше автодополнение и тому подобное. +- Помогаем ИИ-агентам быстрее видеть структуру и принимать более правильные решения, минимизируя случайные ошибки. +- Появляется возможность проверять корректность программы без её запуска, за счёт статической проверки. Такая проверка не гарантирует, что логика программы написана правильно, но, по крайней мере, в ней не будет ошибок типов. + +## Как указывать типы параметров + +JSDoc-комментарий начинается с `/**` и располагается прямо перед определением функции. Тип каждого параметра указывается тегом `@param`, тип возвращаемого значения — тегом `@returns`. Сам тип записывается в фигурных скобках. + +Разберём на примере функции, которая вычисляет сумму двух переданных значений: + +```javascript +/** + * @param {number} a + * @param {number} b + * @returns {number} + */ +function add(a, b) { + return a + b; +} + +console.log(add(2, 3)); // => 5 +``` + +```text +/** + * @param {number} a ← тип параметра a + * @param {number} b ← тип параметра b + * @returns {number} ← тип возвращаемого значения + */ +``` + +Теперь редактор кода будет подсказывать, что функция `add()` принимает два числа и возвращает число. Если попытаться передать строку, редактор подсветит это как проблему и предупредит: + +```javascript +add('2', 3); // Argument of type 'string' is not assignable to parameter of type 'number' +``` + +## Какие типы используются в аннотациях + +На этом этапе достаточно знать аннотации для простых, примитивных типов данных: + +- `number` для чисел — в JavaScript один числовой тип и для целых, и для дробных +- `string` для строк +- `boolean` для логических значений (`true` или `false`) + +```javascript +/** + * @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`. Например, функция может только печатать текст на экран: + +```javascript +/** + * @param {string} name + * @returns {void} + */ +function printGreeting(name) { + console.log(`Hello, ${name}!`); +} + +printGreeting('Anna'); +// => Hello, Anna! +``` + +## Пример с параметрами по умолчанию + +Аннотации работают одинаково как для обязательных параметров, так и для тех, у которых есть значение по умолчанию. Имя необязательного параметра заключается в квадратные скобки, а после знака `=` указывается стандартное значение: + +```javascript +/** + * @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, где аннотации встроены прямо в синтаксис. diff --git a/modules/40-define-functions/350-type-annotations/ru/data.yml b/modules/40-define-functions/350-type-annotations/ru/data.yml new file mode 100644 index 00000000..1d248c31 --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/ru/data.yml @@ -0,0 +1,14 @@ +--- +name: Аннотации типов +tips: + - > + [JSDoc в справочнике + TypeScript](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) + - > + [Метод + repeat()](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/repeat) +definitions: + - name: Аннотация типов + description: явное указание типов параметров и возвращаемого значения функции + - name: Статическая проверка + description: проверка корректности кода без его запуска diff --git a/modules/40-define-functions/350-type-annotations/test.js b/modules/40-define-functions/350-type-annotations/test.js new file mode 100644 index 00000000..a6f8056d --- /dev/null +++ b/modules/40-define-functions/350-type-annotations/test.js @@ -0,0 +1,22 @@ +// @ts-nocheck — в tsconfig нет типов Node.js (types: []), а тест читает исходник через node:fs + +import { readFileSync } from 'node:fs'; +import { expect, test } from 'vitest'; +import f from './index.js'; + +test('test', () => { + const source = readFileSync(new URL('./index.js', import.meta.url), 'utf8'); + const paramAnnotations = source.match(/@param\s*\{/g) ?? []; + expect( + paramAnnotations.length, + 'Укажите JSDoc-аннотации @param для обоих параметров', + ).toBeGreaterThanOrEqual(2); + expect( + source, + 'Укажите JSDoc-аннотацию @returns для возвращаемого значения', + ).toMatch(/@returns\s*\{/); + + expect(f('javascript', 1)).toBe('javascript'); + expect(f('javascript', 3)).toBe('javascriptjavascriptjavascript'); + expect(f('java', 0)).toBe(''); +});