$ sudo teach IT
МОДУЛЬ 2 · УРОК 1

Тип number в TypeScript

Целые числа, дробные числа, NaN, Infinity и методы Math

Добро пожаловать!

Сегодня мы начинаем изучение примитивных типов TypeScript — основы, без которой невозможно написать ни одну строку кода. Первый тип, с которым мы познакомимся, — это number. Это один из самых часто используемых типов в любом языке программирования. Числа окружают нас повсюду: возраст пользователя, цена товара, координаты на карте, количество элементов в массиве. В TypeScript все числа — и целые, и дробные — относятся к одному типу number.

Цели урока

1
Понять, что такое тип number
2
Различать целые и дробные числа
3
Изучить особые значения: NaN и Infinity
4
Освоить основные методы Math

Что такое тип number?

Тип number в TypeScript предназначен для представления всех числовых значений. В отличие от некоторых других языков программирования (например, Java или C++), TypeScript не разделяет целые и дробные числа на отдельные типы. Всё — и 42, и 3.14 — относится к типу number.

Пример: Объявление числовых переменных

// Целые числа
let возраст: number = 25;
let количество_студентов: number = 30;
let код_страны: number = 7;

// Дробные числа
let рост: number = 1.78;
let цена: number = 99.99;
let pi: number = 3.14159;

// Отрицательные числа
let температура: number = -15;
let долг: number = -500.50;

// Научная запись
let Авогадро: number = 6.022e23;  // 6.022 × 10²³

console.log(возраст);          // 25
console.log(цена);             // 99.99
console.log(Авогадро);         // 6.022e+23

Важно понимать

В JavaScript (а значит и в TypeScript) все числа хранятся как 64-битные числа с плавающей запятой (двойной точности). Это означает, что TypeScript может работать с числами от приблизительно -5 × 10⁻³⁰⁸ до 5 × 10³⁰⁸, но точность ограничена approximately 15-17 значащими цифрами.

Целые и дробные числа

Хотя TypeScript объединяет все числа в один тип, важно понимать разницу между целыми и дробными числами, так как это влияет на поведение вычислений и точность результатов.

Целые числа (Integers)

let a: number = 42;
let b: number = -100;
let c: number = 0;

// Шестнадцатеричные
let hex: number = 0xFF;      // 255

// Двоичные
let bin: number = 0b1010;    // 10

// Восьмеричные
let oct: number = 0o77;      // 63

Дробные числа (Floats)

let x: number = 3.14;
let y: number = -0.5;
let z: number = 2.0;  // Также number!

// Научная запись
let small: number = 1.5e-10;  // 0.00000000015
let big: number = 1.5e10;     // 15000000000

Проблема точности

// Классическая проблема с плавающей запятой
let result: number = 0.1 + 0.2;
console.log(result);          // 0.30000000000000004
console.log(result === 0.3);  // false!

// Как решить:
console.log(Math.abs(result - 0.3) < Number.EPSILON);  // true

// Или округлить до нужного количества знаков
letточный: number = Math.round((0.1 + 0.2) * 100) / 100;
console.log(точный);  // 0.3

Это не баг, а особенность представления дробных чисел в компьютере. Все языки программирования, использующие IEEE 754 стандарт, сталкиваются с этой проблемой. Запомните это правило: никогда не сравнивайте дробные числа напрямую!

Особые числовые значения

Помимо обычных чисел, тип number включает несколько особых значений, которые важно знать каждому разработчику.

NaN (Not a Number)

NaN — это значение, которое возникает, когда математическая операция не может дать осмысленный результат. Например, деление нуля на ноль или попытка преобразовать нечисловую строку в число.

// Когда возникает NaN
let a: number = 0 / 0;           // NaN
let b: number = Math.sqrt(-1);   // NaN
let c: number = parseInt("abc");  // NaN
let d: number = Infinity - Infinity;  // NaN

// Свойства NaN
console.log(typeof NaN);          // "number" (!)
console.log(NaN === NaN);         // false (!!)
console.log(isNaN(NaN));          // true
console.log(Number.isNaN(NaN));   // true (более надёжный метод)

// Как проверить на NaN
let значение: number = NaN;

// Правильные способы проверки:
if (Number.isNaN(значение)) {
  console.log("Это NaN!");
}

// Неправильный способ (не используйте это):
if (значение !== значение) {
  console.log("Это тоже NaN!");
}

Почему NaN !== NaN? — Это особенность стандарта IEEE 754. Считается, что если два значения не определены, их нельзя считать равными. Всегда используйте Number.isNaN() для проверки.

Infinity и -Infinity

Infinity и -Infinity представляют бесконечность и отрицательную бесконечность соответственно.

// Когда возникает Infinity
let inf: number = 1 / 0;        // Infinity
let negInf: number = -1 / 0;    // -Infinity
let big: number = 1e308 * 10;   // Infinity

// Свойства
console.log(typeof Infinity);    // "number"
console.log(Infinity > 1000000); // true
console.log(-Infinity < -1000000); // true

// Проверка
console.log(isFinite(42));        // true
console.log(isFinite(Infinity));  // false
console.log(Number.isFinite(Infinity)); // false

// Полезные константы
console.log(Number.MAX_VALUE);    // 1.7976931348623157e+308
console.log(Number.MIN_VALUE);    // 5e-324 (самое маленькое положительное)
console.log(Number.MAX_SAFE_INTEGER);  // 9007199254740991 (2⁵³ - 1)
console.log(Number.MIN_SAFE_INTEGER);  // -9007199254740991

Полезные методы работы с числами

// Преобразование в число
let str1: string = "123";
let num1: number = Number(str1);       // 123
let num2: number = parseInt("45abc");  // 45
let num3: number = parseFloat("3.14"); // 3.14
let num4: number = +"+99.5";           // 99.5

// Проверка типов
console.log(Number.isInteger(42));      // true
console.log(Number.isInteger(3.14));    // false
console.log(Number.isFinite(42));       // true
console.log(Number.isNaN(NaN));         // true

// Округление
let число: number = 3.7;
console.log(Math.round(число));   // 4 (округление)
console.log(Math.ceil(число));    // 4 (округление вверх)
console.log(Math.floor(число));   // 3 (округление вниз)
console.log(Math.trunc(число));   // 3 (отсечение дробной части)
console.log(Math.round(-3.7));    // -4 (округление)

Объект Math

Объект Math предоставляет множество полезных методов и констант для работы с числами. Давайте рассмотрим самые важные из них.

Математические функции

Math.abs(-5)     // 5 (модуль)
Math.sqrt(16)    // 4 (квадратный корень)
Math.pow(2, 10)  // 1024 (степень)
Math.cbrt(27)    // 3 (кубический корень)
Math.log(100)    // 4.605 (натуральный логарифм)
Math.log2(8)     // 3 (логарифм по основанию 2)
Math.log10(100)  // 2 (десятичный логарифм)

Тригонометрия

Math.sin(Math.PI / 2)  // 1
Math.cos(Math.PI)      // -1
Math.tan(0)            // 0
Math.asin(1)           // π/2
Math.acos(-1)          // π
Math.atan(1)           // π/4
Math.atan2(1, 1)       // π/4

Случайные числа

// Случайное число от 0 до 1
Math.random()

// Случайное целое от min до max
function случайное(
  min: number, 
  max: number
): number {
  return Math.floor(
    Math.random() * (max - min + 1) + min
  );
}
случайное(1, 10)  // 1-10

Константы Math

Math.PI    // 3.141592653589793
Math.E     // 2.718281828459045
Math.LN2   // 0.6931471805599453
Math.LN10  // 2.302585092994046
Math.SQRT2 // 1.4142135623730951

Практический пример: Калькулятор площади

function площадьКруга(радиус: number): number {
  return Math.PI * Math.pow(радиус, 2);
}

function площадьТреугольника(
  основание: number, 
  высота: number
): number {
  return 0.5 * основание * высота;
}

function гипотенуза(а: number, б: number): number {
  return Math.sqrt(Math.pow(а, 2) + Math.pow(б, 2));
}

// Использование
console.log(`Площадь круга: ${площадьКруга(5).toFixed(2)}`);
console.log(`Площадь треугольника: ${площадьТреугольника(10, 5)}`);
console.log(`Гипотенуза: ${гипотенуза(3, 4)}`);

Преобразование чисел

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

Способы преобразования в число

// 1. Функция Number()
let a: number = Number("123");      // 123
let b: number = Number("12.5");     // 12.5
let c: number = Number(true);       // 1
let d: number = Number(false);      // 0
let e: number = Number(null);       // 0
let f: number = Number(undefined);  // NaN
let g: number = Number("abc");      // NaN

// 2. Унарный плюс (самый быстрый способ)
let x = +"42";        // 42
let y = +"3.14";      // 3.14
let z = +"abc";       // NaN

// 3. parseInt() - для целых чисел
let p1 = parseInt("123abc");   // 123
let p2 = parseInt("abc123");   // NaN
let p3 = parseInt("101", 2);   // 5 (двоичная система)
let p4 = parseInt("FF", 16);   // 255 (шестнадцатеричная)

// 4. parseFloat() - для дробных чисел
let f1 = parseFloat("3.14abc");  // 3.14
let f2 = parseFloat("abc3.14");  // NaN

Рекомендация

Используйте Number() для общих преобразований, parseInt() когда нужно извлечь целое число из строки, и унарный плюс + для кратких преобразований в понятном контексте. Избегайте new Number() — это создает объект, а не примитив.

Форматирование чисел

TypeScript (и JavaScript) предоставляют несколько способов форматировать числа для вывода на экран.

let число: number = 1234567.891;

// toFixed() - фиксированное количество знаков после запятой
console.log(число.toFixed(2));     // "1234567.89"
console.log(число.toFixed(0));     // "1234568"

// toPrecision() - общее количество значащих цифр
console.log(число.toPrecision(5));  // "1.2346e+6"

// toExponential() - научная запись
console.log(число.toExponential(2)); // "1.23e+6"

// toLocaleString() - форматирование с учётом локали
console.log(число.toLocaleString('ru-RU'));  // "1 234 567,891"
console.log(число.toLocaleString('en-US'));  // "1,234,567.891"

// parseInt и parseFloat для обратного преобразования
let обратно: string = "1234567.891";
console.log(parseInt(обратно));    // 1234567
console.log(parseFloat(обратно));  // 1234567.891

Практический пример: Форматирование валюты

function форматироватьВалюту(
  сумма: number, 
  валюта: string = "₽"
): string {
  return сумма.toLocaleString('ru-RU', {
    style: 'decimal',
    minimumFractionDigits: 2,
    maximumFractionDigits: 2
  }) + " " + валюта;
}

console.log(форматироватьВалюту(1234567.89));   // "1 234 567,89 ₽"
console.log(форматироватьВалюту(42.5));          // "42,50 ₽"
console.log(форматироватьВалюту(0, "$"));        // "0,00 $"

Частые ошибки

Давайте рассмотрим типичные ошибки, которые допускают начинающие разработчики при работе с числами.

Ошибка 1: Сравнение дробных чисел

// Неправильно
if (0.1 + 0.2 === 0.3) { ... }  // Всегда false!

// Правильно
if (Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON) { ... }

Дробные числа с плавающей запятой не могут быть представлены точно в двоичной системе.

Ошибка 2: Потеря точности больших чисел

// Проблема
let bigNumber = 9007199254740992;
console.log(bigNumber === bigNumber + 1);  // true (!)

// Решение: используйте BigInt для очень больших чисел
let huge: bigint = 9007199254740992n;
console.log(huge === huge + 1n);  // false

Числа больше Number.MAX_SAFE_INTEGER теряют точность.

Ошибка 3: Использование == вместо ===

// Проблема
console.log(0 == false);   // true
console.log(0 == "");      // true
console.log("" == false);  // true

// Решение: всегда используйте ===
console.log(0 === false);  // false
console.log(0 === "");     // false

Оператор == выполняет приведение типов, что может привести к неожиданным результатам.

Ошибка 4: Деление на ноль

// Не вызывает ошибку, но даёт Infinity
console.log(1 / 0);      // Infinity
console.log(-1 / 0);     // -Infinity
console.log(0 / 0);      // NaN

// Всегда проверяйте делитель!
function безопасноеДеление(a: number, b: number): number {
  if (b === 0) {
    throw new Error("Деление на ноль невозможно");
  }
  return a / b;
}

Деление на ноль не вызывает ошибку в JavaScript/TypeScript, но даёт Infinity или NaN.

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

Давайте закрепим изученное материал на практических примерах, которые вы сможете использовать в своих проектах.

Пример 1: Калькулятор скидки

function рассчитатьСкидку(
  цена: number, 
  процентСкидки: number
): { стараяЦена: number; новаяЦена: number; экономия: number } {
  if (процентСкидки < 0 || процентСкидки > 100) {
    throw new Error("Процент скидки должен быть от 0 до 100");
  }
  
  const экономия = цена * (процентСкидки / 100);
  const новаяЦена = цена - экономия;
  
  return {
    стараяЦена: цена,
    новаяЦена: Math.round(новаяЦена * 100) / 100,
    экономия: Math.round(экономия * 100) / 100
  };
}

const результат = рассчитатьСкидку(1000, 15);
console.log(`Старая цена: ${результат.стараяЦена}₽`);
console.log(`Новая цена: ${результат.новаяЦена}₽`);
console.log(`Вы сэкономили: ${результат.экономия}₽`);

Пример 2: Генератор паролей

function сгенерироватьПароль(длина: number): string {
  const символы = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%";
  let пароль = "";
  
  for (let i = 0; i < длина; i++) {
    const индекс = Math.floor(Math.random() * символы.length);
    пароль += символы[индекс];
  }
  
  return пароль;
}

console.log(сгенерироватьПароль(12));  // "aB3$kL9!mN2@"

Пример 3: Конвертер единиц измерения

type ЕдиницаДлины = "мм" | "см" | "м" | "км";

const коэффициенты: Record<ЕдиницаДлины, number> = {
  "мм": 0.001,
  "см": 0.01,
  "м": 1,
  "км": 1000
};

function конвертировать(
  значение: number, 
  из: ЕдиницаДлины, 
  в: ЕдиницаДлины
): number {
  const вМетры = значение * коэффициенты[из];
  return вМетры / коэффициенты[в];
}

console.log(конвертировать(100, "см", "м"));    // 1
console.log(конвертировать(1, "км", "м"));      // 1000
console.log(конвертировать(2500, "мм", "км"));  // 0.0025

Подведём итоги

Вы изучили:

  • Тип number — все числа в одном типе
  • Целые и дробные числа
  • Особые значения: NaN и Infinity
  • Методы объекта Math
  • Преобразование и форматирование чисел

Важно помнить:

  • Не сравнивайте дробные числа напрямую
  • Используйте Number.isNaN() вместо isNaN()
  • Проверяйте делитель перед делением
  • Будьте осторожны с большими числами
  • Используйте BigInt для очень больших целых чисел

Тест по типу number

10 вопросов

Вычисление площади круга

Premium