Урок 09

Type guards и сужение

Type guard — это проверка, которая сужает тип. TypeScript понимает такие проверки и даёт доступ к специфичным полям.

typeof

function print(value: string | number) {
    if (typeof value === 'string') {
        console.log(value.toUpperCase());
    } else {
        console.log(value.toFixed(2));
    }
}

Работает с string, number, boolean, symbol, undefined, object, function.

in

Проверяет наличие свойства:

type User = { name: string };
type Admin = { name: string; role: string };

function print(person: User | Admin) {
    if ('role' in person) {
        console.log(person.role);
    }
}

instanceof

Для классов:

class Dog { bark() {} }
class Cat { meow() {} }

function makeSound(animal: Dog | Cat) {
    if (animal instanceof Dog) {
        animal.bark();
    } else {
        animal.meow();
    }
}

Литеральный дискриминатор

type Shape =
    | { kind: 'circle'; radius: number }
    | { kind: 'square'; side: number };

function area(shape: Shape) {
    if (shape.kind === 'circle') {
        return Math.PI * shape.radius ** 2;
    }
    return shape.side ** 2;
}

Пользовательские type guards

Функция, возвращающая value is Type:

function isString(value: unknown): value is string {
    return typeof value === 'string';
}

function print(value: unknown) {
    if (isString(value)) {
        console.log(value.toUpperCase());
    }
}

Возвращаемый тип value is string сообщает TypeScript, что при true тип — string.

Проверка на null

function print(user: User | null) {
    if (user !== null) {
        console.log(user.name);
    }
}

Или через опциональную цепочку:

console.log(user?.name);

Или через нулевое слияние:

const name = user?.name ?? 'Гость';

Проверка на undefined

if (value !== undefined) {
    //
}

if (typeof value !== 'undefined') {
    //
}

Array.isArray

function print(value: string | string[]) {
    if (Array.isArray(value)) {
        value.forEach((v) => console.log(v));
    } else {
        console.log(value);
    }
}

Assertion functions

Функция, которая выбрасывает исключение, если условие не выполнено:

function assert(condition: unknown, message: string): asserts condition {
    if (!condition) throw new Error(message);
}

function print(value: string | null) {
    assert(value !== null, 'Value is null');
    console.log(value.toUpperCase()); // value: string
}

asserts condition — после вызова TypeScript уверен в условии.

Исключающие проверки

type Status = 'idle' | 'loading' | 'success';

function handle(status: Status) {
    if (status === 'idle') return;
    if (status === 'loading') return;
    // здесь status: 'success'
}

Проблема truthiness

function print(value: string | null) {
    if (value) { // отсекает не только null, но и ''
        //
    }
}

Если пустая строка валидна — проверяй явно:

if (value !== null) {
    //
}

Итоги

  • Type guards сужают тип
  • typeof, in, instanceof
  • Дискриминатор — по литеральному полю
  • Пользовательские — value is Type
  • asserts — для проверок
  • ?. и ?? для null/undefined