Как стать автором
Обновить

Знакомство с TypeScript: базовая типизация и ключевые возможности

Уровень сложностиПростой

Почему TypeScript?

Андерс Хейлсберг — именно этого человека принято считать создателем TypeScript. Инженер-программист, который подарил миру такие языки как: Turbo Pascal, Delphi и C#.
Основным мотивом создания TypeScript было желание решить проблемы, связанные с разработкой крупных и сложных приложений на JavaScript.

Думаю, многие кто работал с большими проектами замечал, как порой бывает сложно рефакторить или даже просто понимать структуру данных , а чаще всего вообще невозможно. Именно этими проблемами и "занимается" TypeScript. На данном этапе стоит уточнить, что TypeScript представляет собой расширенную версию JavaScript, содержащую в себе все основные возможности языка JavaScript, дополненные некоторыми расширениями.

Основной причиной использования TypeScript является возможность добавления статической типизации к JavaScript. Переменные с статической типизацией имеют тип, который не может быть изменен после их объявления. Это позволяет предотвратить множество потенциальных ошибок.

Проблемы JavaScript:

meme
meme
  1. Динамические типы.

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

    let x = 10;
    console.log(x + 5);  // Вывод: 15
    x = "10";  // Переопределяем переменную x строкой
    console.log(x + 5);  // Вывод: "105"
  2. Отсутствие Type Safety

    Отсутствие Type Safety в JavaScript означает, что язык не предоставляет строгой проверки типов данных на этапе компиляции или выполнения. В отличие от JavaScript, TypeScript требует, чтобы разработчики явно указывали типы данных для переменных, функций и других элементов кода. Это позволяет предотвратить ошибки, связанные с неправильными типами данных, ещё до запуска программы.

    Чтобы обеспечить безопасность типов данных, TypeScript предоставляет два подхода:

    1. Время выполнения (Runtime): TypeScript предоставляет информацию о типах данных во время выполнения программы, используя API Runtime Type Information (RTTI). Это позволяет проверять типы данных и обрабатывать их во время выполнения, что может быть полезно для некоторых операций.

    2. Время компиляции (Compile Time): TypeScript обеспечивает защиту типов данных на этапе компиляции. Это означает, что множество ошибок, связанных с типами данных, могут быть обнаружены и исправлены ещё до запуска программы. Компилятор TypeScript проверяет соответствие типов данных и предупреждает о возможных проблемах, что помогает создавать более надежный и безопасный код.

    Таким образом, TypeScript предоставляет средства как для обеспечения безопасности типов данных во время выполнения программы, так и для предотвращения ошибок на этапе компиляции, что делает процесс разработки более эффективным и надежным.

  3. Autocomplete

    В JavaScript, автодополнение может быть менее точным из-за динамической природы языка. Редакторы и интегрированные среды разработки (IDE) могут предлагать автодополнение на основе контекста и доступных методов и свойств объектов. Однако, из-за отсутствия строгой статической типизации, автодополнение может быть менее точным и предложения могут быть менее информативными.

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

Пример autocomplete
Пример autocomplete

Начало работы

Для начала работы с TypeScript, нужно либо настроить специальный интерфейс командной строки (CLI), либо воспользоваться онлайн-песочницей или альтернативными инструментами. Для выполнения кода потребуется использовать Node.js. Давайте создадим новый проект:

  1. Создайте новую директорию для проекта и перейдите в неё.

  2. Инициализируйте новый Node.js-проект с помощью npm init -y.

  3. Установите компилятор TypeScript локально для текущего проекта с помощью npm i typescript.

Теперь, чтобы убедиться, что всё работает, создайте файл index.ts с каким-либо кодом и транспилируйте его в JavaScript с помощью npx tsc index.ts. Наконец, запустите скомпилированный код с помощью Node.js.

Обратите внимание, что устанавливать TypeScript лучше локально и использовать его через npx, так как версии могут отличаться, и это поможет избежать проблем совместимости.

Что же такое tsc?

TSC - это сокращение от TypeScript Compiler, то есть компилятор TypeScript. Он отвечает за преобразование файлов с расширением .ts (код на TypeScript) в файлы с расширением .js (код на JavaScript), который может быть исполнен браузером или Node.js.

Несколько основных флагов tsc:

  1. --target: Устанавливает версию JavaScript, на которую будет скомпилирован TypeScript код.

  2. --outFile или -o: Позволяет указать файл, в который будет сохранён скомпилированный JavaScript.

  3. --watch или -w: Запускает компилятор в режиме наблюдения, при котором он следит за изменениями файлов и автоматически перекомпилирует их при необходимости.

  4. --strict: Включает строгий режим, в котором TypeScript проверяет код на более широкий спектр ошибок.

  5. --module или -m: Указывает тип модуля, используемого в JavaScript коде.

Это лишь небольшой набор флагов tsc, который можно использовать для настройки процесса компиляции TypeScript кода под конкретные потребности проекта.

Базовая типизация

Примитивные типы

Если вы ранее работали с JavaScript, думаю, вам не составит большой сложности понять основные типы TypeScript. В отличие от JavaScript, где типы данных могут быть динамически определены во время выполнения программы, TypeScript предлагает статическую типизацию, позволяющую определить типы данных на этапе компиляции. Это означает, что вы можете объявлять типы переменных, параметров функций, возвращаемых значений и других элементов вашего кода заранее, что повышает надежность и понятность вашего кода.

К примитивным типам можно отнести:

  1. Number

    По данному примеру можно увидеть какой синтаксис предоставляет нам TypeScript для типизации переменных. Достаточно просто объявить переменную и через двоеточие указать желаемый тип.

    const age: number = 52;
        
    console.log(age.split(''))
        
    // Property 'split' does not exist on type 'number'.
  2. String

    const city: string = "Питер";
    
    console.log(city + 52); 
    // Это не ошибка , TypeScript понимает , 
    // что у нас в данный момент конкатенация
    
    console.log(Math.pow(city, 3)); 
    // Argument of type 'string' 
    // is not assignable to parameter of type 'number'.
  3. Boolean

    // Пример правильного использования
    
    let x: number = 10;
    let y: number = 5;
    let isGreaterThan: boolean = x > y;
    console.log(isGreaterThan); // true
    
    // Пример с ошибкой
    
     let isRead: boolean = 42;
    // Type 'number' is not assignable to type 'boolean'.
  4. Null

    Часто мы сталкиваемся с таким использованием null. Не стоит беспокоиться о "палке" между типами, подробнее с ней ознакомимся к концу статьи.
    Этот оператор говорит TypeScript о том, что мы предполагаем, что наша переменная может принимать значения одного из перечисленных типов. Здесь сразу хочется отметить, что это не логический оператор || )из JavaScript.

    let myVar: string | null = null;
    
    console.log(myVar); // Вывод: null
    
    myVar = "Hello";
    
    console.log(myVar); // Вывод: Hello
  5. Undefined

    В случае с undefined также применяется оператор объединения типов (|), который позволяет переменной принимать либо определенный тип данных, либо значение undefined.

    let myVar: string | undefined;
    
    console.log(myVar); // Вывод: undefined
    
    myVar = "Hello";
    
    console.log(myVar); // Вывод: Hello
    
    myVar = 42; // Type 'number' is not assignable to type 'string'.
  6. Symbol

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

    let mySymbol: symbol = Symbol("foo");
  7. BigInt

    Тип bigint в TypeScript используется для представления целых чисел произвольной длины. Этот тип добавлен в стандарт ECMAScript 2020 и позволяет работать с числами, которые превышают максимальное значение, представленное типом number.

    let bigIntValue: bigint = 1234567890123456789012345678901234567890n;
  8. Literal Types

    Литералы являются точными значениями, предоставляемыми самим JavaScript, и могут быть использованы в TypeScript для явного указания конкретных значений переменных. Литеральный тип данных в TypeScript представляет собой ограничение набора допустимых значений переменной до определенных литеральных значений.

    // Строковые литералы:
    
    let color: "red" | "green" | "blue";
    color = "red"; // Верно
    color = "yellow"; // Ошибка: Недопустимое значение
    
    // Числовые литералы:
    
    let status: 200 | 404 | 500;
    status = 200; // Верно
    status = 401; // Ошибка: Недопустимое значение
    
    // Логические литералы:
    
    let isEnabled: true | false;
    isEnabled = true; // Верно
    isEnabled = "false"; // Ошибка: Недопустимое значение

Составные типы

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

  1. Object Type

    type Person = {
        name: string;
        age: number;
        isStudent: boolean;
    };

    В этом примере Person представляет собой тип объекта, который состоит из трех свойств: name, age и isStudent, каждое из которых имеет свой собственный тип данных.

  2. Array Type

    let numbers: number[] = [1, 2, 3, 4, 5];
    let names: Array<string> = ["Tom", "Bob", "Alice"];

    В этом примере numbers является массивом чисел (number[]), содержащим элементы типа number. Также представлен альтернативный способ объявления массива с использованием generics. Здесь names - это массив строк (Array<string>), в котором каждый элемент имеет тип string. Более подробно о generics мы поговорим позже.

  3. Tuples

    let tuple: [string, number, boolean];
    tuple = ["hello", 10, true];
    
    let matrix: [number, number, number][] = [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]
    ];

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

  4. Union Types

    type Result = "success" | "error";
    
    function processResult(result: Result) {
        // Обработка результата
    }
    
    processResult("success"); // Верно
    processResult("fail");    // Ошибка: Недопустимое значение

    Union типы также могут использоваться с более сложными типами данных, такими как объекты, перечисления и даже другие union типы.

    В этом примере Result представляет собой union тип, который может быть либо "success", либо "error". Функция processResult принимает аргумент result, который должен быть одним из этих значений.

  5. Intersection Types

    interface Animal {
        name: string;
        age: number;
    }
    
    interface Flyable {
        fly: () => void;
    }
    
    type Bird = Animal & Flyable;
    
    function birdInfo(bird: Bird) {
        console.log(`Name: ${bird.name}`);
        console.log(`Age: ${bird.age}`);
        bird.fly();
    }
    
    let myBird: Bird = {
        name: "Sparrow",
        age: 2,
        fly: () => console.log("Flying...")
    };
    
    birdInfo(myBird);
    

    В этом примере создается интерфейс Animal, которая представляет собой общие свойства для животных, и интерфейс Flyable, которая представляет собой способность к полету. Затем создается новый тип Bird, который представляет собой пересечение типов Animal и Flyable. Таким образом, объекты типа Bird должны содержать свойства как животных, так и способность к полету.

    Затем функция birdInfo принимает объект типа Bird и выводит информацию о птице, включая ее имя и возраст, а также вызывает метод fly, который представлен в интерфейсе Flyable.

    Intersection Types полезны, когда нужно объединить свойства из нескольких типов данных в один тип. Это позволяет создавать более сложные типы данных, которые содержат комбинацию функциональности из различных источников.

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

Специальные типы данных

meme
meme
  1. Any

    Тип any позволяет переменной принимать значения любого типа данных. Использование any отключает проверку типов во время компиляции TypeScript.

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

    let myVar: any;
    myVar = 10;         // число
    myVar = "hello";    // строка
    myVar = true;       // булево значение
  2. Unknown

    Тип unknown представляет значение, о типе которого мы ничего не знаем. Он более безопасен, чем any, потому что нельзя просто так использовать значение типа unknown.

    unknown часто используется в ситуациях, когда тип данных неизвестен и требуется безопасная обработка значений разных типов.

    let userInput: unknown;
    userInput = 10;     // число
    userInput = "hello";// строка
    
    // Необходимо выполнить дополнительные проверки перед использованием значения типа unknown
    if (typeof userInput === 'string') {
        console.log(userInput.toUpperCase());
    }
  3. Void

    Тип void используется для указания на то, что функция не возвращает никакого значения.

    Функция logMessage ничего не возвращает (возвращаемый тип - void), она просто выводит сообщение на консоль.

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

    function logMessage(message: string): void {
        console.log(message);
    }
  4. Never

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

    Функция throwError бросает ошибку и никогда не возвращает управление.

    never также используется в случаях, когда функция содержит бесконечный цикл или всегда выбрасывает ошибку.

    function throwError(message: string): never {
        throw new Error(message);
    }
    
  5. object

    Тип object представляет любой объект JavaScript: это может быть объект, массив, функция, класс и т. д. Но он не включает в себя примитивные типы данных (number, string, boolean, symbol, null или undefined).

    let obj: object;
    obj = { name: "John", age: 30 };   // объект
    obj = [1, 2, 3];                    // массив
    obj = function() { console.log("Hello"); }; // функция
    

Подведение итогов

Разумный выбор TypeScript обоснован множеством факторов. Во-первых, его строгая типизация обеспечивает более надежный код и предотвращает множество потенциальных ошибок на этапе разработки. Это позволяет сократить время, затраченное на отладку, и снизить вероятность возникновения проблем в процессе эксплуатации. Кроме того, TypeScript поддерживает современные возможности JavaScript, что позволяет использовать последние языковые конструкции и функции. Его активное сообщество и широкие возможности интеграции с другими технологиями делают его привлекательным выбором для разработчиков, стремящихся к созданию масштабируемых и поддерживаемых проектов в сфере веб-разработки.

Также, хочется оставить удобную шпаргалку, которую я нашел на просторах Интернета, к которой вы можете обратиться, если что-то забыли или, наоборот, узнать новое, чего не было сказано в статье.

Шпаргалка
Шпаргалка

Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.