Как использовать защиту типов в TypeScript

Источник: «How to use type guards in TypeScript»
TypeScript использует некоторые встроенные операторы JavaScript, такие, как typeof, instanceof и оператор in, которые используются для определения того, содержит ли объект свойство.

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

TypeScript использует некоторые встроенные операторы JavaScript, такие, как typeof, instanceof и оператор in, которые используются для определения того, содержит ли объект свойство. Защитники типов позволяют указать компилятору TypeScript вывести определённый тип переменной в определённом контексте, гарантируя, что тип аргумента соответствует тому, что вы говорите.

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

Существует пять основных способов использования защиты типа:

В этой статье мы рассмотрим четыре перечисленных выше метода. Давайте приступим!

Защита типа instanceof

instanceof — это встроенная защита типа, которую можно использовать для проверки того, является ли значение экземпляром заданной функции-конструктора или класса. С помощью этой защиты типа мы можем проверить, является ли объект или значение производным от класса, что полезно для определения типа экземпляра типа.

Ниже приведён основной синтаксис для защиты типа instanceof:

objectVariable instanceof ClassName;

В приведённом ниже примере мы видим пример защиты типа instanceof:

interface Accessory {
brand: string;
}
class Necklace implements Accessory{
kind: string;
brand: string;
constructor(brand: string, kind: string) {
this.brand = brand;
this.kind = kind;
}
}
class bracelet implements Accessory{
brand: string;
year: number;
constructor(brand: string, year: number) {
this.brand = brand;
this.year = year;
}
}
const getRandomAccessory = () =>{
return Math.random() < 0.5 ?
new bracelet('cartier', 2021) :
new Necklace('choker', 'TASAKI');
}
let Accessory = getRandomAccessory();
if (Accessory instanceof bracelet) {
console.log(Accessory.year);
}
if (Accessory instanceof Necklace) {
console.log(Accessory.brand);
}

Приведённая выше функция getRandomAccessory возвращает объект Necklace или bracelet, поскольку они оба реализуют интерфейс Accessory. Подписи конструкторов для Necklace и bracelet различны, и защита типа instanceof сравнивает обе подписи конструкторов для эффективного определения типа.

Защита типа typeof

Ограничитель типа typeof используется для определения типа переменной. Считается, что защита типа typeof является очень ограниченной и неглубокой. Он может определять только следующие типы, распознаваемые JavaScript:

Для всего, что не входит в этот список, защитник типа typeof просто возвращает объект.

Защитник типа typeof может быть записан следующими двумя способами:

typeof v !== "typename"
#or
typeof v === "typename"

typename может быть string, number, symbol или boolean.

В приведённом ниже примере переменная StudentId имеет тип объединения параметров string | number. Мы видим, что если переменная является string, то выводится Student, а если это число, то выводится Id. Защитный элемент typeof помогает нам извлечь тип из x:

function StudentId(x: string | number) {
if (typeof x == 'string') {
console.log('Student');
}
if (typeof x === 'number') {
console.log('Id');
}
}
StudentId(`446`); //выводит Student
StudentId(446); //выводит Id

Защита типа in

Защита типа in проверяет, обладает ли объект определённым свойством, используя его для различения разных типов. Обычно он возвращает логическое значение, которое указывает, существует ли свойство у данного объекта. Он используется для сужения возможностей, а также для проверки поддержки браузером.

Ниже приведён основной синтаксис для защиты типа in:

propertyName in objectName

В приведённом ниже примере защитник типа in проверяет, существует ли свойство house. В тех случаях, когда он существует, возвращается логическое значение true, а когда не существует, возвращается false.

"house" in { name: "test", house: { parts: "door" } }; // => true
"house" in { name: "test", house: { parts: "windows" } }; // => true
"house" in { name: "test", house: { parts: "roof" } }; // => true
"house" in { name: "test" }; // => false
"house" in { name: "test", house: undefined }; // => true

Другой подобный пример работы защиты типа in показан ниже:

interface Pupil {
ID: string;
}
interface Adult {
SSN: number;
}
interface Person {
name: string;
age: number;
}
let person: Pupil | Adult | Person = {
name: 'Britney',
age: 6
};
const getIdentifier = (person: Pupil | Adult | Person) => {
if ('name' in person) {
return person.name;
}
else if ('ID' in person) {
return person.ID
}
return person.SSN;
}

Защита типа сужения равенства

Сужение равенства проверяет значение выражения. Для того чтобы две переменные были равны, обе переменные должны иметь одинаковый тип. Если тип переменной неизвестен, но она равна другой переменной с точным типом, то TypeScript сузит тип первой переменной с помощью информации, которую предоставляет известная переменная:

function getValues(a: number | string, b: string) {
if (a === b) {
// здесь происходит сужение, сужение до string
console.log(typeof a) // string
} else {
// если сужение не происходит, тип остаётся неизвестным
console.log(typeof a) // number или string
}
}

Если переменная a равна переменной b, то обе они должны иметь одинаковый тип. В данном случае TypeScript сужает его до string. Без сужения тип a остаётся неясным, потому что это может быть либо number, либо string.

Пользовательская защита типа с предикатом

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

Пример пользовательской защиты типов показан ниже:

interface Necklace{
kind: string;
brand: string;
}
interface bracelet{
brand: string;
year: number;
}
type Accessory = Necklace | bracelet;

const isNecklace = (b: Accessory): b is Necklace => {
return (b as Necklace).kind !== undefined
}
const Necklace: Accessory = {kind: "Choker", brand: "TASAKI"};
const bracelet: Accessory = {brand: "Cartier", year: 2021};
console.log(isNecklace(bracelet)) //Выводит false
console.log(isNecklace(Necklace)) //Выводит true

В приведённом выше коде предикат типа b is Necklace заставит TypeScript уменьшить тип до Necklace вместо того, чтобы вернуть просто логическое значение.

Заключение

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

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

Надеюсь, вам понравилась эта статья!

Дополнительные материалы

Предыдущая Статья

Как использовать CSS aspect-ratio

Следующая Статья

Поиск в PDF-файлах с помощью MySQL и Laravel