Изучение проверки типов TypeScript: комплексное руководство по обеспечению безопасности типов

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

Содержание:

  1. Аннотации базового типа
  2. Вывод типа
  3. Сигнатуры функций
  4. Типы объединений
  5. Типы пересечений
  6. Типовая защита
  7. Утверждение типа
  8. Предикаты типов
  9. Обобщенные параметры и параметры типов
  10. Условные типы
  11. keyof и типы поиска
  12. Псевдонимы типов и интерфейсы
  13. Совместимость типов

Метод 1: базовые аннотации типов
TypeScript позволяет явным аннотациям типов объявлять ожидаемые типы переменных, параметров функций и возвращаемых значений. Например:

let age: number = 25;
let name: string = "John Doe";

Метод 2: определение типа
TypeScript может выводить типы на основе присвоенных значений. Если тип не указан явно, TypeScript попытается определить его на основе контекста. Например:

let age = 25; // TypeScript infers the type as number
let name = "John Doe"; // TypeScript infers the type as string

Метод 3: сигнатуры функций
TypeScript позволяет указывать типы параметров функции и возвращаемые значения. Это помогает предотвратить ошибки, связанные с типом. Например:

function addNumbers(a: number, b: number): number {
  return a + b;
}

Метод 4: типы объединения
Типы объединения позволяют значению иметь несколько типов. Это полезно, когда переменная может содержать разные типы значений. Например:

let result: number | string;
result = 10; // valid
result = "success"; // valid

Метод 5: Типы пересечений
Типы пересечений объединяют несколько типов в один тип. Это полезно, когда мы хотим, чтобы значение имело свойства нескольких типов. Например:

type Admin = {
  name: string;
  role: string;
};
type User = {
  name: string;
  age: number;
};
type AdminUser = Admin & User;

Метод 6: защита типа
Защита типа позволяет сузить тип переменной внутри условного блока. Это полезно при работе с типами объединения. Например:

function printLength(value: string | string[]): void {
  if (typeof value === "string") {
    console.log(value.length); // valid
  } else {
    console.log(value.length); // valid
  }
}

Метод 7: утверждение типа
Утверждение типа позволяет переопределить тип, выведенный TypeScript. Это полезно, когда мы знаем о типе больше, чем может сделать TypeScript. Например:

let message: any = "Hello World!";
let length: number = (message as string).length;

Метод 8: предикаты типов
Предикаты типов — это пользовательские функции, которые возвращают логическое значение для подтверждения типа переменной. Это полезно при работе с неизвестными типами. Например:

function isString(value: any): value is string {
  return typeof value === "string";
}
function processValue(value: unknown) {
  if (isString(value)) {
    console.log(value.length); // valid
  }
}

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

function identity<T>(arg: T): T {
  return arg;
}
let result = identity<string>("Hello");

Метод 10: Условные типы
Условные типы позволяют создавать типы, зависящие от условия. Это помогает создавать сложные сопоставления типов. Например:

type NonNullable<T> = T extends null | undefined ? never : T;

Метод 11: keyof и типы поиска
Оператор keyofпозволяет получить ключи типа, которые можно использовать для создания типов поиска. Это полезно при работе со свойствами объекта. Например:

type Person = {
  name: string;
  age: number;
};
type PersonKeys = keyof Person; // "name" | "age"
type PersonName = Person["name"]; // string

Метод 12: Псевдонимы типов и интерфейсы
Псевдонимы типов и интерфейсы позволяют определять собственные типы. В большинстве случаев их можно использовать взаимозаменяемо. Например:

type Point = {
  x: number;
  y: number;
};
interface User {
  name: string;
  age: number;
}