Построение сопоставленного эксклюзивного типа в машинописном тексте

У меня есть следующий тип (упрощенный):

type ValueRepresents = {
    boolean: true
    number?: false
    other?: false
} |
{
    boolean?: false
    number: true
    other?: false
} |
{
    boolean?: false
    number?: false
    other: true
}

Мой фактический тип имеет гораздо больше возможных ключей. Есть ли способ сгенерировать этот тип из списка возможных ключей, чтобы сделать допустимым только один ключ со значением true? Что-то вроде:

type ValueTypes = "boolean" | "number" | "other"
type ValueRepresents <T extends ValueTypes> = {
    [k in ValueTypes]: k extends T ? true : false
}
const a: ValueRepresents<"boolean"> = {
    boolean: true,
    number: false,
    other: false,
}

Но я стремлюсь использовать:

// should pass
const a: ValueRepresents = { boolean: true }

// should pass
const a2: ValueRepresents = {
    boolean: true,
    number: false,
}

// should error
const a3: ValueRepresents = {
    boolean: true,
    number: true,
}

// should error
const a4: ValueRepresents = {}

Я также пробовал следуя этому ответу, но пока не добился успеха:

type ValueRepresents <T extends ValueTypes> = {
    [k in Exclude<T, ValueTypes>]?: false
} & { [k in T]: true }

person AJP    schedule 28.06.2021    source источник


Ответы (1)


Вы можете попробовать создать тип объединения, подобный этому

type ValueTypes = "boolean" | "number" | "other"

type ValueRepresents = ({
    [K in ValueTypes]: Partial<Record<Exclude<ValueTypes, K>, false>> & Record<K, true>
})[ValueTypes]

TypeScript Playground


TL;DR

Я не думаю, что это выражение имеет конкретное название. Я видел подобные примеры, используемые в документах Advanced Types, но я попытаюсь объяснить, как это работает.

type ValueRepresents = {
    [K in ValueTypes]: Partial<Record<Exclude<ValueTypes, K>, false>> & Record<K, true>
}

создает тип, эквивалентный:

type ValueRepresents = {
  boolean: {
    boolean: true;
    number?: false;
    other?: false;
  };
  number: {
    boolean?: false;
    number: true;
    other?: false;
  };
  other: {
    boolean?: false;
    number?: true;
    other: true;
  };
};

и, добавляя тип объединения в квадратных скобках [ValueTypes], он извлекает значения этих (всех) ключей в другом типе объединения, эквивалентном:

type ValueRepresents =
  | {
      boolean: true;
      number?: false;
      other?: false;
    }
  | {
      boolean?: false;
      number: true;
      other?: false;
    }
  | {
      boolean?: false;
      number?: true;
      other: true;
    };
person Teneff    schedule 28.06.2021
comment
Это блестяще. Спасибо. Я никогда не видел этот синтаксис до )[ValueTypes], не могли бы вы указать мне на некоторые документы, объясняющие его, пожалуйста? Я поиграл с ним сейчас, поэтому у меня есть частичное представление о том, что он делает, но я не знаю, как он называется. - person AJP; 29.06.2021
comment
@AJP Я добавил краткое объяснение того, как это работает - person Teneff; 29.06.2021