Освоение защиты типов в TypeScript: повышение безопасности вашего кода
Защита типов в TypeScript — это мощная функция, которая позволяет разработчикам сузить тип переменной во время выполнения. Вам следует использовать защиту типов, чтобы обеспечить безопасность типов в вашем коде.
В этом посте мы рассмотрим различные способы использования защиты типов в TypeScript и обсудим, как они могут помочь сделать ваш код более безопасным и надежным.
Используя защиту типов, разработчики могут улучшить качество своего кода и снизить риск ошибок во время выполнения, вызванных неожиданными типами.
Понимание защиты типов в TypeScript
Защита типа (Type Guard) — это процесс проверки типа переменной в TypeScript. Существуют различные способы реализации защиты типов, включая использование операторов typeof
, instanceof
и in
или создание пользовательских средств защиты типов.
Защита типов особенно полезна при работе с типами объединения, сторонними библиотеками, проверкой пользовательского ввода и сложными структурами данных.
В TypeScript существует 5 типов защиты типов:
-
typeof
-
in
-
instanceof
- Тип защиты сужения равенства
- Пользовательский тип защиты, определяемый пользователем
Мы подробно обсудим каждую из этих техник на примерах.
Использование typeof
Type Guard в TypeScript
Оператор typeof
— это встроенная защита типа в TypeScript, которая проверяет тип переменной во время выполнения. Вы должны использовать это ключевое слово перед именем переменной.
Он возвращает строку, представляющую тип переменной, что позволяет разработчикам сузить тип переменной в условном блоке.
const firstName = 'Max' typeof firstName // It will return "string"
Одним из наиболее распространенных случаев использования typeof
защиты типа является проверка примитивных типов, таких как number
, string
, boolean
, undefined
, function
, object
и symbol
.
Вы можете проверить, соответствует ли тип переменной нужному типу. В зависимости от условия мы можем выполнить некоторые действия.
const printInfo = (value: string | number) => { if (typeof value === 'number') { console.log(`Student ID: ${value}`) } else { console.log(`Student name: ${value}`) } } printInfo(23) // Student ID: 23 printInfo('Max') // Student name: Max
В этом примере у меня есть функция printInfo
, которая принимает один параметр. Параметр value
может быть либо a, string
либо a number
.
Внутри функции вы можете проверить тип этого параметра, используя typeof
защиту типа, чтобы определить, является ли он number
или string
.
Если value
это число, функция выводит сообщение, указывающее, что это идентификатор студента, а также фактическое значение value
.
С другой стороны, если value
это строка, функция выводит сообщение, указывающее, что это имя студента, а также фактическое значение value
.
Использование in
оператора в качестве защиты типа
В TypeScript вы можете использовать этот in
оператор в качестве средства защиты типа, чтобы определить, имеет ли объект определенное свойство или подпись индекса. Это вернет логическое значение.
Если свойство существует в этом объекте, оно вернет true, в противном случае — false. Вот почему мы можем использовать этот оператор с условными операторами.
Давайте рассмотрим функцию, которая принимает один параметр, который может содержать два типа объектов. Вы хотите выполнять различные операции в зависимости от типа объекта.
interface Circle { kind: 'circle' radius: number } interface Square { kind: 'square' sideLength: number } type Shape = Circle | Square const calculateArea = (shape: Shape) => { if ('radius' in shape) { // shape is now narrowed down to Circle type return Math.PI * shape.radius ** 2 } else { // shape is now narrowed down to Square type return shape.sideLength ** 2 } }
У меня есть два интерфейса Circle
, Square
которые описывают свойства круга и квадрата соответственно. Функция calculateArea
принимает shape
параметр, который может быть либо Circle
либо Square
.
Теперь вы можете проверить, присутствует ли свойство радиуса в объекте фигуры или нет, с помощью in
оператора TypeScript. Если он существует, это означает, что фигура представляет собой файл Circle
.
Вы можете использовать формулу для расчета площади этого круга. Если свойство радиуса не существует, то форма представляет собой квадрат. Используйте другую формулу, чтобы вычислить его площадь.
Одним из ограничений этого метода является то, что имена свойств необходимо вводить в виде строки. TypeScript не сможет давать вам подсказки. Возможно, вы ввели неправильное имя свойства.
Использование instanceof
оператора в качестве защиты типа
Оператор instanceof
в TypeScript — это защита, позволяющая нам проверить, является ли объект экземпляром определенного класса или функции-конструктора.
Если объект является экземпляром класса, он вернет true
, в противном случае false
— . Он будет соответствовать, если объект содержит аналогичные свойства и методы этого конкретного класса или функции-конструктора .
class Car { startEngine() { console.log('Starting car engine...') } } class Truck { startEngine() { console.log('Starting truck engine...') } loadCargo() { console.log('Loading cargo...') } } const useVehicle = (vehicle: Car | Truck) => { vehicle.startEngine() if (vehicle instanceof Truck) { vehicle.loadCargo() } } const car = new Car() const truck = new Truck() useVehicle(car) // Starting car engine... useVehicle(truck) // Starting truck engine... // Loading cargo...
В этом примере у нас есть два класса Car
и Truck
. Оба класса имеют startEngine
метод, но только у Truck
класса есть дополнительный loadCargo
метод.
Он useVehicle
принимает параметр vehicle
, который может быть либо Car
либо Truck
. Внутри этой функции мы вызываем startEngine
метод для vehicle
параметра, независимо от того, является ли он Car
или Truck
. Потому что у них обоих есть этот метод.
Но если вы попытаетесь вызвать loadCargo
метод, TypeScript выдаст ошибку. Потому что этот метод доступен только в Track
классе. Вот почему вам нужно проверить, является ли параметр vehicle
экземпляром класса, Truck
используя instanceof
оператор.
Тип защиты равенства
Как вы знаете, когда переменная хранит несколько типов объектов, вам необходимо использовать защиту типа, чтобы гарантировать, что вы получаете доступ к свойствам правильного объекта.
Имя общего свойства можно использовать в качестве защиты типа для определения правильного типа объекта.
Вы должны указать уникальное значение для этого общего свойства. В отличие от in
оператора, в этом методе вы получите предложения TypeScript .
interface Circle { kind: 'circle' radius: number } interface Square { kind: 'square' sideLength: number } type Shape = Circle | Square const calculateArea = (shape: Shape) => { if (shape.kind === 'circle') { console.log('Circle radius: ', shape.radius) } else { console.log('Square length: ', shape.sideLength) } }
У нас есть 2 интерфейса Circle
и Square
. У них обоих есть одно общее свойство kind
: . Мы также использем уникальное значение для этого свойства. Внутри Circle
интерфейса kind
свойство имеет значение « круг », а внутри Square
интерфейса — значение « квадрат ».
Функция calculateArea()
принимает параметр, который может быть либо типом Circle
, либо Square
. Вы можете получить доступ к kind
свойству из этого параметра формы, поскольку оба интерфейса имеют это свойство.
Если оно имеет значение circle
, мы знаем, что это Circle
, и можем безопасно получить доступ к этому radius
свойству. В противном случае это должен быть знак, Square
и мы сможем безопасно получить доступ к sideLength
собственности.
type Shape = 'circle' | 'square' const calculateArea = (shape: Shape) => { if (shape === 'circle') { console.log("It's a circle") } else { console.log("It's a square") } }
Вы также можете использовать эту технику без объектов. Здесь у меня есть тип объединения с некоторыми значениями. Теперь вы можете выполнять некоторую логику на основе значения.
Пользовательский тип защиты, определяемый пользователем
Настраиваемые пользователем средства защиты типов в TypeScript позволяют разработчикам определять свои собственные средства защиты типов с определенной логикой. Эти охранники типа определяются как функции, которые принимают значение неизвестного типа и возвращают логическое значение.
В этом примере у нас есть два интерфейса Person
и Company
, которые имеют общее свойство name
. Мы определяем функцию защиты пользовательского типа isCompany
, которая проверяет, имеет ли данный объект свойство industry
или нет, и возвращает логическое значение.
interface Person { name: string } interface Company { name: string industry: string } const isCompany = (obj: Person | Company): obj is Company => { return (obj as Company).industry !== undefined } const printDetails = (obj: Person | Company) => { console.log(`Name: ${obj.name}`) if (isCompany(obj)) { console.log(`Industry: ${obj.industry}`) } } const person: Person = { name: 'Alice' } const company: Company = { name: 'ABC Ltd.', industry: 'Technology' } printDetails(person) // Name: Alice printDetails(company) // Name: ABC Ltd. // Industry: Technology
Эта isCompany
функция принимает объект с типом объединения Person | Company
. Внутри функции я проверяю, имеет ли данный объект свойство, industry
используя !==
оператор. Если свойство существует, мы возвращаем true
, в противном случае false
— .
В printDetails
функции я беру объект типа Person | Company
в качестве аргумента. Мы используем isCompany
функцию защиты типа, чтобы сузить тип этого объекта. Если isCompany
возвращается true
, мы можем безопасно получить доступ к industry
свойству.
Когда следует использовать защиту типа в TypeScript?
Охранники типов в TypeScript полезны, когда вам нужно сузить тип переменной во время выполнения. Они помогают гарантировать типобезопасность вашего кода и снижают риск ошибок во время выполнения.
Вам следует использовать защиту типа в TypeScript в следующих сценариях:
- При работе с типами объединения: типы объединения могут быть проблематичными, поскольку TypeScript может не знать точный тип переменной во время выполнения. Охранники типа могут помочь сузить тип переменной на основе определенного условия.
- При работе со сторонними библиотеками: вам может потребоваться использовать средства защиты типа, чтобы проверить, соответствуют ли значения, возвращаемые сторонней библиотекой, ожидаемому типу.
- При проверке пользовательского ввода: защита типа может быть полезна для проверки пользовательского ввода, чтобы гарантировать, что ввод имеет ожидаемый тип.
- При работе со сложными структурами данных: защита типов может помочь избежать ошибок при работе со сложными структурами данных, такими как объекты с вложенными свойствами или массивы объектов.
Преимущества использования защиты типов TypeScript
Использование защиты типов в TypeScript может дать несколько преимуществ для безопасности типов и качества кода:
- Улучшенная безопасность типов: средства защиты типов помогают гарантировать, что переменные имеют ожидаемый тип, снижая риск ошибок во время выполнения, вызванных неожиданными типами. Это делает ваш код более устойчивым и надежным.
- Лучшее качество кода: средства защиты типов делают ваш код более читабельным и самодокументируемым, предоставляя больше информации о типах переменных. Это может помочь другим разработчикам легче понять ваш код, что приведет к повышению качества кода и его удобства сопровождения.
- Сокращение времени отладки. Обнаруживая ошибки типа во время компиляции, а не во время выполнения, средства защиты типов могут сократить время и усилия, необходимые для отладки кода. Это может сэкономить вам и вашей команде драгоценное время и ресурсы.
- Улучшение опыта разработки. Использование средств защиты типов может помочь вам обнаружить ошибки на ранних этапах процесса разработки, что приведет к более плавному и приятному процессу разработки. Это также может помочь улучшить вашу общую производительность.
Заключение
Овладение защитой типов в TypeScript может значительно улучшить безопасность типов и качество кода ваших проектов. Они позволяют сузить тип переменной на основе ее значения во время выполнения, что упрощает написание типобезопасного кода.
Я обсудил несколько способов реализации защиты типов в вашем проекте TypeScript. Большую часть времени мы используем встроенные средства защиты, такие как typeof
, in
оператор, instanceof
оператор и т. д.
Но если у вас есть какие-то особые потребности в проекте, вы также можете написать свои собственные функции защиты типов. Каждый подход имеет свои преимущества и ограничения, и важно выбрать правильный вариант для конкретного случая использования.