TypeScript Курс

Функции TypeScript и сигнатуры типов

Самоучитель Python 3, собранный из материалов данного сайта.Предназначен в основном для тех, кто хочет изучить язык программирования Python с нуля.

Введение: Что такое функции и зачем они нужны?

Представь, что ты строишь робота, который должен уметь выполнять разные команды. Вместо того чтобы каждый раз заново объяснять роботу, как сделать бутерброд, ты даёшь ему одну команду "сделайБутерброд()", и он выполняет все шаги автоматически. В программировании такие команды называются функциями.

Функции — это как маленькие программы внутри твоей основной программы. Они помогают:

  • Не повторять код — написал один раз, используешь много раз
  • Упрощать чтение — вместо кучи непонятного кода — понятные имена функций
  • Исправлять ошибки — нужно изменить логику? Меняешь только в одном месте!
  • Делить работу — разные функции могут писать разные программисты

В TypeScript функции особенно круты, потому что мы можем указать, какие данные они принимают и что возвращают. Это помогает избежать ошибок ещё до запуска программы!

Основы функций в TypeScript

Как объявить функцию?

Давай начнём с самого простого примера. Представь, что мы пишем программу для школы, где нужно приветствовать учеников:

// Функция, которая приветствует ученика
function приветствие() {
    console.log("Привет, ученик!");
    console.log("Добро пожаловать на урок!");
}

// Вызываем функцию
приветствие(); // Выведет: Привет, ученик! Добро пожаловать на урок!
приветствие(); // Можно вызывать сколько угодно раз

Обрати внимание на структуру:

  • function — ключевое слово, которое говорит "я создаю функцию"
  • приветствие — имя функции (да, в TypeScript можно использовать русские буквы!)
  • () — пустые скобки означают, что функция не принимает данных
  • { ... } — тело функции, то, что она делает

Функции с параметрами

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

// Функция с параметром "имя"
function приветствиеПоИмени(имя: string) {
    console.log("Привет, " + имя + "!");
    console.log("Рады тебя видеть на уроке!");
}

// Вызываем с разными именами
приветствиеПоИмени("Анна"); // Привет, Анна! Рады тебя видеть на уроке!
приветствиеПоИмени("Пётр"); // Привет, Пётр! Рады тебя видеть на уроке!

: string — это указание типа. Мы говорим TypeScript: "параметр имя должен быть строкой". Если попробуешь передать число — TypeScript предупредит об ошибке!

Функции с возвратом значения

Иногда функция должна не просто что-то сделать, а ещё и вернуть результат. Например, посчитать среднюю оценку:

// Функция, которая возвращает число
function вычислитьСреднее(оценка1: number, оценка2: number, оценка3: number): number {
    let сумма = оценка1 + оценка2 + оценка3;
    let среднее = сумма / 3;
    return среднее; // return - возвращаем результат
}

// Используем результат функции
let средняяОценка = вычислитьСреднее(4, 5, 3);
console.log("Средняя оценка: " + средняяОценка); // Средняя оценка: 4

// Можно сразу вставлять в выражение
console.log("Удвоенное среднее: " + (вычислитьСреднее(5, 5, 4) * 2)); // 9.333...

Обрати внимание на : number после скобок — это тип возвращаемого значения. Если функция ничего не возвращает, пишут : void (пустота).

Продвинутые концепции функций

Необязательные параметры

Иногда параметр может быть не обязательным. Например, у ученика может быть не указан класс:

// Параметр "класс" необязательный (знак ?)
function представитьУченика(имя: string, класс?: string) {
    if (класс) {
        console.log(`Ученик ${имя} из ${класс} класса`);
    } else {
        console.log(`Ученик ${имя} (класс не указан)`);
    }
}

представитьУченика("Мария", "7А"); // Ученик Мария из 7А класса
представитьУченика("Иван");        // Ученик Иван (класс не указан)

Символ ? после имени параметра делает его необязательным. Внутри функции мы проверяем, есть ли значение.

Параметры по умолчанию

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

// Параметр "оценка" по умолчанию равен 4
function оценитьРаботу(ученик: string, оценка: number = 4) {
    console.log(`Работа ученика ${ученик} оценена на ${оценка}`);
}

оценитьРаботу("Анна", 5); // Работа ученика Анна оценена на 5
оценитьРаботу("Пётр");    // Работа ученика Пётр оценена на 4 (значение по умолчанию)

Rest-параметры (остаточные параметры)

Что если мы не знаем, сколько будет оценок? Используем ... (три точки):

// ...оценки - массив из любого количества чисел
function вычислитьСреднееМного(...оценки: number[]): number {
    let сумма = 0;
    for (let оценка of оценки) {
        сумма += оценка;
    }
    return сумма / оценки.length;
}

console.log(вычислитьСреднееМного(4, 5, 3, 5, 4)); // 4.2
console.log(вычислитьСреднееМного(5, 5));         // 5
console.log(вычислитьСреднееМного(3));            // 3

Стрелочные функции (Arrow Functions)

В современном TypeScript часто используют короткую запись функций — стрелочные функции. Они похожи на стрелу: =>.

// Обычная функция
function сложить(a: number, b: number): number {
    return a + b;
}

// Стрелочная функция (то же самое)
const сложитьСтрелка = (a: number, b: number): number => {
    return a + b;
};

// Если тело функции состоит из одного return, можно ещё короче:
const сложитьКоротко = (a: number, b: number): number => a + b;

console.log(сложить(3, 4));      // 7
console.log(сложитьСтрелка(3, 4)); // 7
console.log(сложитьКоротко(3, 4)); // 7

Стрелочные функции особенно удобны, когда нужно передать функцию в другую функцию (об этом позже).

Типизация функций

Тип функции

В TypeScript можно указать тип самой функции. Это как описание: "эта функция принимает два числа и возвращает число":

// Тип функции: (a: number, b: number) => number
let операция: (a: number, b: number) => number;

// Теперь в эту переменную можно сохранить любую функцию с таким типом
операция = (x: number, y: number): number => x + y;
console.log(операция(10, 5)); // 15

операция = (x: number, y: number): number => x * y;
console.log(операция(10, 5)); // 50

// Ошибка! Неправильный тип
// операция = (x: string) => x.length; // Ошибка!

Callback-функции (функции обратного вызова)

Это когда одна функция принимает другую функцию как параметр. Например, у нас есть массив оценок, и мы хотим применить к каждой оценке какое-то преобразование:

// Функция, которая применяет преобразование к каждой оценке
function обработатьОценки(оценки: number[], преобразование: (оценка: number) => number): number[] {
    let результат: number[] = [];
    for (let оценка of оценки) {
        результат.push(преобразование(оценка));
    }
    return результат;
}

// Разные преобразования
const удвоить = (x: number): number => x * 2;
const добавитьБонус = (x: number): number => x + 1;

let оценки = [3, 4, 5];
console.log(обработатьОценки(оценки, удвоить));      // [6, 8, 10]
console.log(обработатьОценки(оценки, добавитьБонус)); // [4, 5, 6]

// Можно передать стрелочную функцию напрямую
console.log(обработатьОценки(оценки, (x) => x * x)); // [9, 16, 25]

Практические примеры из жизни

Пример 1: Калькулятор карманных денег

// Функция для расчёта карманных денег на неделю
function рассчитатьКарманныеДеньги(
    бюджетНаДень: number,
    дни: number,
    скидкаЗаПомощь?: number  // необязательный параметр
): number {
    let всего = бюджетНаДень * дни;
    
    // Если помогал по дому — скидка (бонус)
    if (скидкаЗаПомощь) {
        всего += скидкаЗаПомощь;
        console.log("Отлично! Ты получил бонус за помощь!");
    }
    
    return всего;
}

// Используем функцию
let недельныйБюджет = рассчитатьКарманныеДеньги(100, 7); // 700 рублей
console.log("Без помощи: " + недельныйБюджет); // Без помощи: 700

let сПомощью = рассчитатьКарманныеДеньги(100, 7, 200); // 900 рублей
console.log("С помощью: " + сПомощью); // С помощью: 900

Пример 2: Игра "Угадай число"

// Функция для проверки догадки
function проверитьДогадку(загаданное: number, догадка: number): string {
    if (догадка === загаданное) {
        return "Поздравляю! Ты угадал! 🎉";
    } else if (догадка < загаданное) {
        return "Слишком мало! Попробуй больше.";
    } else {
        return "Слишком много! Попробуй меньше.";
    }
}

// Функция для генерации случайного числа
function загадатьЧисло(от: number, до: number): number {
    return Math.floor(Math.random() * (до - от + 1)) + от;
}

// Играем
let секретноеЧисло = загадатьЧисло(1, 100);
console.log("Я загадал число от 1 до 100!");

let попытка1 = проверитьДогадку(секретноеЧисло, 50);
console.log("Попытка 50: " + попытка1);

let попытка2 = проверитьДогадку(секретноеЧисло, 75);
console.log("Попытка 75: " + попытка2);

// И так далее...

Пример 3: Фильтрация списка друзей

// Тип для описания друга
type Друг = {
    имя: string;
    возраст: number;
    город: string;
};

// Функция для фильтрации друзей по условию
function отфильтроватьДрузей(
    друзья: Друг[],
    условие: (друг: Друг) => boolean
): Друг[] {
    let результат: Друг[] = [];
    for (let друг of друзья) {
        if (условие(друг)) {
            результат.push(друг);
        }
    }
    return результат;
}

// Наши друзья
let моиДрузья: Друг[] = [
    { имя: "Анна", возраст: 13, город: "Москва" },
    { имя: "Пётр", возраст: 14, город: "Санкт-Петербург" },
    { имя: "Мария", возраст: 12, город: "Москва" },
    { имя: "Иван", возраст: 15, город: "Казань" }
];

// Фильтруем: друзья из Москвы
let друзьяИзМосквы = отфильтроватьДрузей(
    моиДрузья,
    (друг) => друг.город === "Москва"
);
console.log("Друзья из Москвы:");
for (let друг of друзьяИзМосквы) {
    console.log(`- ${друг.имя}, ${друг.возраст} лет`);
}

// Фильтруем: друзья старше 13 лет
let старшиеДрузья = отфильтроватьДрузей(
    моиДрузья,
    (друг) => друг.возраст > 13
);
console.log("Друзья старше 13 лет:");
for (let друг of старшиеДрузья) {
    console.log(`- ${друг.имя}, ${друг.возраст} лет`);
}

Область видимости переменных

Важно понимать, что переменные внутри функции видны только внутри неё. Это называется локальной областью видимости:

let глобальнаяПеременная = "Я видна всем!";

function тестОбластиВидимости() {
    let локальнаяПеременная = "Я видна только внутри функции";
    console.log(глобальнаяПеременная); // Работает!
    console.log(локальнаяПеременная);  // Работает!
}

тестОбластиВидимости();

console.log(глобальнаяПеременная); // Работает!
// console.log(локальнаяПеременная); // Ошибка! Не видна снаружи

Это защищает переменные от случайного изменения. Ты можешь быть уверен, что внутри функции переменные не "вытекут" наружу.

Чистые функции

В программировании есть важное понятие — чистая функция. Это функция, которая:

  • Всегда возвращает одинаковый результат для одинаковых входных данных
  • Не меняет ничего вне себя (не имеет побочных эффектов)
// Чистая функция - всегда возвращает одно и то же для одинаковых входных данных
function сложитьЧисто(a: number, b: number): number {
    return a + b;
}

// Нечистая функция - использует внешнюю переменную
let внешняяПеременная = 10;
function сложитьНечисто(a: number): number {
    return a + внешняяПеременная; // Зависит от внешнего мира!
}

console.log(сложитьЧисто(2, 3)); // Всегда 5
console.log(сложитьЧисто(2, 3)); // Всегда 5

console.log(сложитьНечисто(5)); // 15
внешняяПеременная = 20;
console.log(сложитьНечисто(5)); // 25 - результат изменился!

Старайся писать чистые функции — они надёжнее и их легче тестировать.

Рекурсия: функция вызывает саму себя

Иногда функция может вызывать саму себя. Это называется рекурсией. Например, подсчёт факториала числа (5! = 5 * 4 * 3 * 2 * 1):

// Рекурсивная функция для вычисления факториала
function факториал(n: number): number {
    // Базовый случай: если n = 1, возвращаем 1
    if (n === 1) {
        return 1;
    }
    // Рекурсивный случай: n * факториал(n-1)
    return n * факториал(n - 1);
}

console.log("5! = " + факториал(5)); // 5! = 120
console.log("3! = " + факториал(3)); // 3! = 6

Рекурсия работает как матрёшка: открываешь большую, а там поменьше, и так до самой маленькой. Но будь осторожен — если не написать базовый случай, функция будет вызывать себя бесконечно!

Практические задания

Теперь твоя очередь! Попробуй выполнить эти задания самостоятельно. Если что-то не получается — перечитай урок ещё раз.

Задание 1: Калькулятор оценок

Напиши функцию вычислитьИтоговуюОценку, которая принимает:

  • Оценку за домашние работы (число от 1 до 5)
  • Оценку за контрольную (число от 1 до 5)
  • Оценку за проект (число от 1 до 5)

Функция должна возвращать среднее арифметическое, округлённое до целого числа. Используй Math.round() для округления.

// Пример использования:
let итог = вычислитьИтоговуюОценку(4, 5, 3);
console.log("Итоговая оценка: " + итог); // Должно вывести 4

Задание 2: Генератор паролей

Создай функцию сгенерироватьПароль, которая принимает:

  • Длину пароля (число)
  • Нужны ли цифры (булево значение, по умолчанию true)
  • Нужны ли спецсимволы (булево значение, по умолчанию false)

Функция должна возвращать строку — сгенерированный пароль. Для простоты используй только буквы латинского алфавита (a-z). Если нужны цифры — добавляй случайные цифры. Если спецсимволы — добавляй символы !@#$%.

// Подсказка: используй Math.random() и String.fromCharCode()
// Пример:
let пароль = сгенерироватьПароль(8, true, true);
console.log("Пароль: " + пароль); // Например: "a7B#k2P!"

Задание 3: Сортировка списка задач

У тебя есть список задач. Каждая задача — это объект с полями:

  • название (строка)
  • приоритет (число от 1 до 5, где 1 — самый важный)
  • выполнена (булево значение)

Напиши функцию отсортироватьЗадачи, которая принимает массив задач и возвращает новый массив, где:

  • Сначала идут невыполненные задачи
  • Внутри каждой группы (выполненные/невыполненные) задачи отсортированы по приоритету (сначала важные)

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

// Пример данных:
let задачи = [
    { название: "Сделать уроки", приоритет: 1, выполнена: false },
    { название: "Помыть посуду", приоритет: 3, выполнена: true },
    { название: "Убрать в комнате", приоритет: 2, выполнена: false },
    { название: "Почитать книгу", приоритет: 4, выполнена: true }
];

let отсортированные = отсортироватьЗадачи(задачи);
console.log("Отсортированные задачи:");
for (let задача of отсортированные) {
    console.log(`[${задача.выполнена ? "✓" : " "}] ${задача.название} (приоритет: ${задача.приоритет})`);
}
// Должно вывести:
// [ ] Сделать уроки (приоритет: 1)
// [ ] Убрать в комнате (приоритет: 2)
// [✓] Помыть посуду (приоритет: 3)
// [✓] Почитать книгу (приоритет: 4)

Заключение

Функции — это основа любого серьёзного программирования. Они помогают делать код:

  • Понятным — хорошее имя функции рассказывает, что она делает
  • Многоразовым — написал один раз, используешь везде
  • Надёжным — TypeScript проверяет типы, чтобы ты не ошибся

В TypeScript у функций есть много возможностей: параметры по умолчанию, необязательные параметры, стрелочные функции, типизация. Используй их, чтобы писать качественный код!

Помни: хорошая функция делает одну вещь и делает её хорошо. Если функция становится слишком большой — разбей её на несколько маленьких.

Попробуй выполнить задания, а потом придумай свои собственные функции. Удачи в программировании! 🚀