Создайте защиту типа для типа объединения

#typescript

Вопрос:

У меня есть такой типаж:

 type BaseType = {
  type: string
  id: string
}
 

Я создаю из него два типа:

 type TypeA = {
  type: 'TypeA'
}

type TypeB = {
  type: 'TypeB'
}
 

Затем я создаю тип объединения TypeA и TypeB :

 type ABTypes = TypeA | TypeB
 

Затем я создаю другой тип на основе BaseType :

 type TypeC = {
  type: 'TypeC'
}
 

Теперь я хочу создать защиту типа для ABTypes такого:

 function isABTypes(
  x: BaseType
): x is isABTypes {
  // I don't want to do this:
  return x.type === 'TypeA' || x.type === 'TypeB'
  // nor:
  return x.type !== 'TypeC'
}
 

Как я могу реализовать защиту типов без необходимости проверять type свойство на наличие «TypeA» и TypeB (которые здесь нетипизированы и не безопасны для рефакторинга)?

Я мог бы использовать это:

 type Types = ABTypes['type']
 

что было бы 'TypeA' | 'TypeB' .

Но это тип, а не строковый массив, который я могу использовать для сравнения.

Самое близкое, что я мог получить, это

 const ABTypes = ['TypeA', 'TypeB']

function isABTypes(
  x: BaseType
): x is isABTypes {
  return ABTypes.inclues(x.type)
}
 

Но это все еще не очень надежно, когда добавляются новые случаи объединения.

Комментарии:

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

Ответ №1:

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

 // this is our base type
interface BaseType {
    type: string;
    id: string;
}

// a list of types
const ABTypes = ["TypeA", "TypeB"] as const;
// union created from above ABTypes -> type ABTypesUnion = "TypeA" | "TypeB"
type ABTypesUnion = typeof ABTypes[number];

// interface with fixed value of type
interface ABTypes extends BaseType {
    type: ABTypesUnion;
}

// test
function isABTypes(x: BaseType): x is ABTypes {
    return ABTypes.includes(x.type as ABTypesUnion);
}

const test: ABTypes = {
    type: "TypeA",
    id: "UX0001"
};

const isTestAB =  isABTypes(test);

 

Вам не нужно поддерживать объединение отдельно.