криптосообщество тратит 1 миллион на возрождение вдохновленного Илоном Маском токена floki-inu.jpg

Сужение типа машинописного текста

Исходный узел: 1857482

Много раз нам нужно иметь возможность различать интерфейсы Typescript, которые имеют некоторые общие свойства. Возьмем следующий пример:

interface Vehicle { weight: number; wheels?: number; class?: 'A' | '1' | '2' | '3';
}

В нашем приложении у нас есть разные транспортные средства. Например, для грузовиков мы хотим отслеживать их вес и количество колес. Однако другие транспортные средства (например, яхты) не имеют колес, но имеют другие атрибуты, которые мы хотим знать, например, их класс, который представляет собой строку, которая может принимать значения «A», «1», «2» или «3». .

Мы могли бы подумать, что хорошая конфигурация может быть следующей:

interface Truck { weight: number; wheels: number;
} interface Yatch { weight: number; class: 'A' | '1' | '2' | '3';
} export type Vehicle = Truck | Yatch;

Однако с этой настройкой у нас возникнет проблема при попытке использовать Vehicle объекты:

Как мы видим, Typescript не может решить, является ли входной объект Truck или Yatch, и выдает следующую ошибку:

Property 'wheels' does not exist on type 'Yatch'

Как мы можем сузить тип объекта? Есть как минимум 3 варианта:

1. Создайте новый type свойство на каждом из интерфейсов

interface Truck { weight: number; wheels: number; type: 'truck';
} interface Yatch { weight: number; class: 'A' | '1' | '2' | '3'; type: 'yatch';
} export type Vehicle = Truck | Yatch;

Это известно как дискриминационные союзы в машинописном тексте. Это решение было бы наиболее адекватным, если бы наши интерфейсы уже имели type атрибут заранее.

Когда используешь Vehicle объектов, теперь мы можем использовать случай переключения, чтобы различать оба интерфейса:

Если мы используем классы вместо интерфейсов для наших типов, следующий альтернативный синтаксис дает точно такой же результат:

class Truck { weight: number; wheels: number; readonly type = 'truck';
} class Yatch { weight: number; class: 'A' | '1' | '2' | '3'; readonly type = 'yatch';
} export type Vehicle = Truck | Yatch;

2. Используйте защиту типа

Тип охранников — это функции, помогающие сузить тип.

interface Truck { weight: number; wheels: number;
} interface Yatch { weight: number; class: 'A' | '1' | '2' | '3';
} export type Vehicle = Truck | Yatch; export function isTruck(arg: any): arg is Truck { return !!arg.weight && !!arg.wheels;
} export function isYatch(arg: any): arg is Yatch { return !!arg.weight && !!arg.class;
}

If isTruck(object) Возвращает true, это означает, что наш объект действительно Truck. Мы можем импортировать и использовать эти функции в любом месте нашего приложения:

Однако с этой настройкой есть небольшая проблема: мы все еще можем построить Vehicle предметы, которых нет Truck ни Yatch:

3. Используйте «никогда»

Чтобы решить эту последнюю проблему, мы можем использовать домен never тип, представленный в Typescript 2.0. Если интерфейс имеет свойство типа never, то это свойство не может быть определено ни для одного объекта, следующего за этим интерфейсом.

interface Truck { weight: number; wheels: number; class?: never;
} interface Yatch { weight: number; wheels?: never; class: 'A' | '1' | '2' | '3';
} export type Vehicle = Truck | Yatch;

Ограничители шрифтов работают точно так же, как и раньше, но теперь мы не можем создавать Vehicle предметы, обладающие одновременно wheels и class свойства:

Как мы видим, мы получаем следующую ошибку:

Type 'number' is not assignable to type 'never'

Проверьте испанскую версию этого поста в моем блоге cibetrucos.com.

Источник: https://www.codementor.io/davidsilvasanmartin/typescript-distributed-unions-1jhqp24mvx.

Отметка времени:

Больше от Сообщество Codementor