Как разрешить ввод пропса при условии что другой пропс был заполнен?

Рейтинг: 0Ответов: 2Опубликовано: 01.05.2023

подскажите пожалуйста, как описать тип, чтобы при отстутсвии определенного ключа, запрещать ввод другого?)

interface Base {
  top?: number;
  left?: number;
  behavior?: "auto" | "smooth"
}

interface OffsetTopProps {
  offsetTop?: number;
}

interface OffsetLeftProps {
  offsetLeft?: number;
}

// в случае если top был заполнен, то offsetTop можно вводить offsetTop?: number; аналогично с left

пробовал через union сделать, но не получилось

type test = Base | OffsetTopProps | OffsetLeftProps 

Ответы

▲ 0

Вот такое решение в итоге:

type TopOptions =
  | { top: number; offsetTop?: number; isConsiderHeader?: boolean }
  | { top?: never; offsetTop?: never; isConsiderHeader?: never };

type LeftOptions = { left: number; offsetLeft?: number } | { left?: never; offsetLeft?: never };

type Options = ScrollToOptions & TopOptions & LeftOptions;

конечно получаем немного кривые ошибки от ts, но в остальном, вроде все работает

▲ -1

Cамое простое — это использовать миксины и объединения одновременно, миксинами задаем 4 возможных варианта:

  • left и top не заданы
  • только left задан
  • только top задан
  • left и top заданы

а union позволит TS выбирать между ними. Все работает как нужно в качестве ошибок, но есть и ложка дегтя в этом решении — ошибки отображаются не совсем точно, например:

const t3: test = {
  offsetTop: 1
}

Type '{ offsetTop: number; }' is not assignable to type 'test'. Type '{ offsetTop: number; }' is missing the following properties from type 'Base4': top, left

const t4: test = {
  offsetLeft: 1
}

Type '{ offsetLeft: number; }' is not assignable to type 'test'. Type '{ offsetLeft: number; }' is missing the following properties from type 'Base4': top, left

(Другие варианты проходят без ошибок можно посмотреть тут)

Если такая точность ошибки не смущает, решение можно считать рабочим.

Весь код:

type Top = {
  top: number;
  offsetTop?: number;
}

type Left = {
  left: number;
  offsetLeft?: number;
}

type Behavior =  {
  behavior?: "auto" | "smooth"
} 

interface Base1 extends Behavior {}
interface Base2 extends Top, Behavior {}
interface Base3 extends Left, Behavior {}
interface Base4 extends Top, Left, Behavior {}

type test  = Base1 | Base2 | Base3 | Base4