Эта статья была впервые опубликована на konadu.dev

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

Представьте, что вы учитель, и в вашем классе учатся мальчики и девочки. Вы хотите разделить класс на две группы: одну группу для мальчиков и одну группу для девочек. Вы также хотите поручить им разные задачи в зависимости от их пола. Как определить, какой ученик мальчик, а какой девочка?

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

Таким образом, защита типа сообщает TypeScript, что в определенной области тип переменной или выражения сужается до определенного подтипа. Защита типа обычно представляет собой условный оператор, который проверяет некоторое свойство или значение, позволяющее различать разные типы. Например, предположим, что у нас есть функция, которая принимает параметр типа string | число. Мы хотим вызвать метод toUpperCase, если это строка, или метод toFixed, если это число. Как мы можем указать TypeScript, какой метод использовать? Используя защиту типа, проверьте приведенный ниже код:

function format(param: string | number) {
 if (typeof param === "string") {
 // TypeScript knows that param is a string here
 return param.toUpperCase();
 } else {
 // TypeScript knows that param is a number here
 return param.toFixed(2);
 }
}

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

В TypeScript существует три основных типа защиты типов. В этой статье мы рассмотрим три основных метода защиты типов в TypeScript.

Типовая защита `typeof`

Представьте, что вы шеф-повар и у вас есть кухня, полная ингредиентов. Некоторые ингредиенты — это соль, сахар, мука, яйца, масло и т. д. Иногда у вас есть рецепт, который требует ингредиента одного из двух типов, например сыра или хлеба. Чтобы использовать правильный метод, вам необходимо знать точный тип ингредиента. Например, предположим, что у вас есть рецепт, в котором используется ингредиент типа «сыр | хлеб`, и вы хотите его растопить, если это сыр, или поджарить, если это хлеб. Как определить, какой тип ингредиента у вас есть?

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

Именно так работает оператор `type of`. У каждого значения в TypeScript есть тип (так же, как вкус каждого ингредиента), который сообщает нам, что это за данные и что мы можем с ними делать. Давайте посмотрим, как эта аналогия работает в TypeScript.

Например, строка — это тип, представляющий текст, и мы можем использовать для нее такие методы, как toUpperCase или Slice. Число — это тип, который представляет числовое значение, и мы можем использовать для него такие методы, как toFixed или Math.sqrt.

Некоторые типы называются примитивными типами, что означает, что они являются основными строительными блоками TypeScript. В TypeScript есть шесть примитивных типов:

  • нить
  • число
  • логическое значение
  • символ
  • неопределенный
  • нулевой

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

Иногда у нас есть переменная или выражение, которое может иметь более одного возможного типа, и нам нужно знать точный тип, чтобы выполнить над ней какую-либо операцию. Например, предположим, что у нас есть функция, которая принимает параметр типа `string | number`, и мы хотим вызвать для него метод toUpperCase, если это строка, или метод toFixed, если это число. Как мы можем указать TypeScript, какой метод использовать?

Здесь на помощь приходит оператор typeof. Оператор typeof — это способ проверки примитивного типа значения. Он возвращает строку, представляющую примитивный тип, например «строка», «число», «логическое значение», «неопределенный» или «объект». Мы можем использовать его, чтобы проверить, имеет ли переменная или выражение определенный примитивный тип, значение NULL или неопределенное значение. Например:

function format(value: string | number): string {
 // A function that formats a value based on its type
 if (typeof value === "string") {
 // If the typeof operator returns "string", we know that the value is a string
 return value.toUpperCase();
 } else if (typeof value === "number") {
 // If the typeof operator returns "number", we know that the value is a number
 return value.toFixed(2);
 } else {
 // If the typeof operator returns anything else, we know that the value is null or undefined
 return "Invalid value";
 }
}
format("Hello"); // This will return "HELLO" because the value is a string and the toUpperCase method is used
format(3.14159); // This will return "3.14" because the value is a number and the toFixed method is used with 2 as the argument
format(null); // This will return "Invalid value" because the value is null and the typeof operator returns "object"

Охранник типа `instanceof`

Иногда нам нужно проверить тип объекта, созданного из класса или функции-конструктора. Класс похож на проект или шаблон, который определяет, как создавать и использовать объекты определенного типа. Функция-конструктор — это уникальная функция, которая создает и инициализирует объекты определенного класса. Объект, созданный из класса или функции-конструктора, называется экземпляром объекта. Например, мы можем создать экземпляр объекта класса, используя ключевое слово new и функцию-конструктор.

Представьте, что вы плотник и у вас есть мастерская, полная инструментов и материалов. Некоторые инструменты и материалы имеют разные свойства и методы, например дрели, отвертки, шурупы, столы, стулья, полки и т. д. Это непримитивные типы в TypeScript, производные от примитивных типов или созданные пользователем с помощью класса. . Например, дрель — это тип, который имеет такие свойства, как мощность и скорость, а также такие методы, как включение или выключение.

Иногда для проекта требуется инструмент или материал одного из двух типов, например дрель или отвертка. Вам необходимо знать точный тип инструмента или материала, чтобы использовать правильный метод. Например, предположим, что у вас есть проект, в котором используется инструмент типа `drill | отвертка`, и вы хотите ее включить, если это дрель, или покрутить, если это отвертка. Как определить, какой тип инструмента у вас есть?

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

Оператор `instanceof` также является способом проверки (дифференциации по физическим различиям в роли плотника) типа класса экземпляра объекта. Он проверяет, является ли объект экземпляром класса или функцией-конструктором, просматривая цепочку его прототипов. Цепочка прототипов — это серия ссылок, которые соединяют объект с его родительским классом и родительским классом его родителя и так далее, пока он не достигнет класса Object, который является основным классом для всех объектов в TypeScript. Оператор экземпляра возвращает true, если он находит класс или функцию-конструктор в цепочке прототипов объекта, и false в противном случае. Также оператор экземпляра можно использовать со встроенными классами или функциями-конструкторами, такими как Array, Date или RegExp. Например, мы можем написать что-то вроде этого:

class Drill {
 // A blueprint that defines how to create and use objects that are drills
 power: number;
 speed: number;
constructor(power: number, speed: number) {
 // A special function that creates and sets up drill objects
 this.power = power;
 this.speed = speed;
 }
turnOn(): void {
 // A method that turns on the drill
 console.log("The drill is on");
 }
turnOff(): void {
 // A method that turns off the drill
 console.log("The drill is off");
 }
}
class Screwdriver {
 // A blueprint that defines how to create and use objects that are screwdrivers
 size: number;
 shape: string;
constructor(size: number, shape: string) {
 // A special function that creates and sets up screwdriver objects
 this.size = size;
 this.shape = shape;
 }
twist(): void {
 // A method that twists the screwdriver
 console.log("The screwdriver is twisting");
 }
}
let tool1 = new Drill(1000, 3000); // Create a new drill object using the constructor function
let tool2 = new Screwdriver(5, "Phillips"); // Create a new screwdriver object using the constructor function
// Check the type of tool1 and use the methods of the Drill class
if (tool1 instanceof Drill) { //Note this code will onry run when tool1 is an instance of Drill
 tool1.turnOn(); // This will turn on the drill
 tool1.turnOff(); // This will turn off the drill
}
// Check the type of tool2 and use the methods of the Screwdriver class
if (tool2 instanceof Screwdriver) { //Note this code will onry run when tool2 is an instance of Screwdriver
 tool2.twist(); // This will twist the screwdriver
}
console.log(tool1 instanceof Drill); // This will return true because tool1 is an instance of the Drill class
console.log(tool2 instanceof Screwdriver); // This will also return true because tool2 is an instance of the Screwdriver class
console.log(tool1 instanceof Object); // This will return true because tool1 is also an instance of the Object class, which is the parent class of all classes in TypeScript
console.log(tool2 instanceof Object); // This will also return true because tool2 is also an instance of the Object class
console.log(tool1 instanceof Screwdriver); // This will return false because tool1 is not an instance of the Screwdriver class
console.log(tool2 instanceof Drill); // This will also return false because tool2 is not an instance of the Drill class

Тип защиты `in`

Представьте, что у вас есть библиотека книг. У некоторых книг есть название, автор, жанр и количество страниц; некоторые нет. Можно сказать, что название, автор, жанр и количество страниц являются свойствами книги. Вы можете думать о книге как об объекте, а о свойствах как об именах атрибутов, описывающих объект. Теперь предположим, что в вашей библиотеке много книг. Оператор in в TypeScript позволяет проверить, имеет ли объект свойство, принадлежащее определенному типу. Вы можете использовать оператор `in`, чтобы задавать вопросы о книгах и библиотеке.

Например, вы можете использовать оператор in, чтобы проверить, есть ли у книги свойство title. Для этого вы пишете «title in book», где «title» — это имя свойства, которое вы хотите проверить, а «book» — это объект, который вы хотите проверить. Оператор in вернет true или false в зависимости от того, есть ли у книги название.

Одной из особенностей TypeScript является то, что он позволяет вам определять типы для ваших переменных и выражений, которые подобны меткам, сообщающим вам, какие данные они могут хранить или создавать. Например, вы можете определить тип под названием «Человек» с такими свойствами, как имя, возраст и род занятий.

Иногда вам следует проверить, имеет ли переменная или выражение определенное свойство или принадлежит ли она определенному типу. Например, чтобы проверить, имеет ли переменная person свойство с именем name или имеет ли она тип Person. Для этого вы можете использовать оператор `in`.

Оператор in принимает два операнда: левый операнд — это имя свойства, которое вы хотите проверить, а правый операнд — это переменная или выражение, которое вы хотите проверить. Оператор in возвращает логическое значение, то есть оно может быть либо «истина», либо «ложь». Например, если вы напишете `name in person`, оператор in проверит, имеет ли переменная person свойство name, и вернет true или false соответственно.

Вы также можете использовать оператор in, чтобы проверить, принадлежит ли переменная или выражение к определенному типу. Для этого вам нужно использовать ключевое слово typeof, которое возвращает имя типа переменной или выражения. Например, если вы напишете `typeof person in Person`, оператор in проверит, является ли тип переменной person `Person`, и вернет true или false соответственно.

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

Вот пример использования оператора in в TypeScript:

// Define a type called Person
type Person = {
 name: string;
 age: number;
 occupation: string;
};
// Create a variable called person of type Person
let person: Person = {
 name: "Alice",
 age: 25,
 occupation: "Software Engineer",
};
// Check if person has a property called name
console.log(name in person); // true
// Check if person has a property called hobby
console.log(hobby in person); // false
// Check if person is of type Person
console.log(typeof person in Person); // true
// Check if person is of type string
console.log(typeof person in string); // false
// Check if person has a property called name
if (name in person) {
 // If true, print the name of the person
 console.log("The name of the person is " + person.name); // The name of the person is Alice
} else {
 // If false, print a message
 console.log("The person does not have a name"); // This will not be printed
}
// Check if person has a property called hobby
if (hobby in person) {
 // If true, print the hobby of the person
 console.log("The hobby of the person is " + person.hobby); // This will not be printed
} else {
 // If false, print a message
 console.log("The person does not have a hobby"); // The person does not have a hobby
}
// Check if person is of type Person
if (typeof person in Person) {
 // If true, print the type of the person
 console.log("The type of the person is Person"); // The type of the person is Person
} else {
 // If false, print a message
 console.log("The type of the person is not Person"); // This will not be printed
}
// Check if person is of type string
if (typeof person in string) {
 // If true, print the type of the person
 console.log("The type of the person is string"); // This will not be printed
} else {
 // If false, print a message
 console.log("The type of the person is not string"); // The type of the person is not string
}

Заключение

В этой статье мы узнали о средствах защиты типов в TypeScript, которые представляют собой способы проверки и сужения типов переменных и выражений. Мы рассмотрели три типа защиты типов: «typeof», «instanceof» и «in».

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

Привет 👋, я думаю, вам понравилась эта статья и вы узнали что-то новое и ценное. Вы можете подписаться на меня в Твиттере (или, скорее, X 😂), где я делюсь советами и приемами, которые помогут вам стать лучшим инженером-программистом.