Type Guard в TypeScript: сужение типов

В этом уроке вы узнаете о Type Guard в TypeScript и сужении типов. Ограничители типов позволяют сузить тип переменной в условном блоке.

typeof

Давайте посмотрим на следующий пример:

type alphanumeric = string | number;

function add(a: alphanumeric, b: alphanumeric) {
    if(typeof a === 'number' && typeof b === 'number') {
        return a + b;
    }

    if(typeof a === 'string' && typeof b === 'string') {
        return a.concat(b);
    }

    throw new Error('Invalid arguments. Both arguments must be either numbers or strings.');
}

Как это устроено:

  • Сначала определите тип alphanumeric, который может содержать либо строку, либо число.
  • Затем объявите функцию, которая добавляет две переменные a и b с типом alphanumeric.
  • Затем проверьте, являются ли оба типа аргументов числами, используя оператор typeof. Если да, то вычислить сумму аргументов с помощью оператора +.
  • После этого проверьте, являются ли оба типа аргументов строками, используя оператор typeof. Если да, то соедините два аргумента.
  • Наконец, выдайте ошибку, если аргументы не являются ни числами, ни строками.

В этом примере TypeScript знает об использовании оператора typeof в условных блоках. Внутри следующего блока if TypeScript понимает, что a и b — это числа.

if(typeof a === 'number' && typeof b === 'number') {
    return a + b;
}

Точно так же в следующем блоке if TypeScript обрабатывает a и b как строки, поэтому вы можете объединить их в одну:

if(typeof a === 'string' && typeof b === 'string') {
    return a.concat(b);
}

instanceof

Подобно оператору typeof, в TypeScript используется оператор instanceof.

Пример:

class Customer {
    isCreditAllowed(): boolean {
        // ...
        return true;
    }
}

class Supplier {
    isInShortList(): boolean {
        // ...
        return true;
    }
}

type BusinessPartner = Customer | Supplier;

function signContract(partner: BusinessPartner) : string {
    let message: string;
    if (partner instanceof Customer) {
        message = partner.isCreditAllowed() ? 'Sign a new contract with the customer' : 'Credit issue';
    }

    if (partner instanceof Supplier) {
        message = partner.isInShortList() ? 'Sign a new contract the supplier' : 'Need to evaluate further';
    }

    return message;
}

Как это устроено:

  • Сначала объявите Customer и Supplier.
    Во-вторых, создайте псевдоним типа BusinessPartner, который является типом объединения Customer и Supplier.
  • В-третьих, объявите функцию signContract(), которая принимает параметр типа BusinessPartner.
  • Наконец, проверьте, является ли партнер экземпляром Customer или Supplier, а затем предоставьте соответствующую логику.

Внутри следующего блока if TypeScript знает, что партнер является экземпляром типа Customer благодаря оператору instanceof :

if(partner instanceof Customer) {
    message = partner.isCreditAllowed() ? 'Sign a new contract with the customer' : 'Credit issue';
}

Точно так же TypeScript знает, что партнер является экземпляром Supplier внутри следующего блока if :

if(partner instanceof Supplier) {
    message = partner.isInShortList() ? 'Sign a new contract with the supplier' : 'Need to evaluate further';
}

Когда if сужает один тип, TypeScript знает, что внутри else это не тот тип, а другой. Например:

function signContract(partner: BusinessPartner) : string {
    let message: string;
    if(partner instanceof Customer) {
        message = partner.isCreditAllowed() ? 'Sign a new contract with the customer' : 'Credit issue';
    } else {
        // must be Supplier
        message = partner.isInShortList() ? 'Sign a new contract with the supplier' : 'Need to evaluate further';
    }
    return message;
}

Оператор in

Оператор in выполняет безопасную проверку существования свойства объекта. Вы также можете использовать его в качестве защиты типа. Например:

function signContract(partner: BusinessPartner) : string {
    let message: string;
    if('isCreditAllowed' in partner) {
        message = partner.isCreditAllowed() ? 'Sign a new contract with the customer' : 'Credit issue';
    } else {
        // must be Supplier
        message = partner.isInShortList() ? 'Sign a new contract the supplier ' : 'Need to evaluate further';
    }
    return message;
}

Определяемые пользователем типы защиты

Определяемые пользователем охранники типа позволяют определить охрану типа или помочь TypeScript вывести тип при использовании функции.

Определяемая пользователем функция защиты типа — это функция, которая просто возвращает arg is aType. Например:

function isCustomer(partner: any): partner is Customer {
    return partner instanceof Customer;
}

В этом примере isCustomer() — определяемая пользователем функция защиты типа. Теперь вы можете использовать его следующим образом:

function signContract(partner: BusinessPartner): string {
    let message: string;
    if(isCustomer(partner)) {
        message = partner.isCreditAllowed() ? 'Sign a new contract with the customer' : 'Credit issue';
    } else {
        message = partner.isInShortList() ? 'Sign a new contract with the supplier' : 'Need to evaluate further';
    }

    return message;
}

Используйте typeof и instanceof для реализации защиты типов в условных блоках.

Рейтинг
( 1 оценка, среднее 5 из 5 )
Александр Русаков / автор статьи
Программист, разработчик, 12 лет опыта работы в крупных компаниях. Быстро освоил typescript, делюсь своими знаниями на страницах этого сайта.
Загрузка ...
JavaScript и TypeScript