Я использую статические объекты для описания определений контрактов и пытаюсь генерировать типы на их основе. Эти контракты могут дополнительно иметь некоторые опции, которые я хочу описать, используя массив имен опций. Затем я хочу использовать этот массив для создания типа для передачи значений параметров. Все параметры должны быть обязательными. См. следующий код:
const definition = { optionNames: ['a', 'b'] as const }
type OptionNames = typeof definition['optionNames'][number]
// type OptionNames = "a" | "b"
type Options = Record<OptionNames, string>
// type Options = {
// a: string;
// b: string;
// }
Пока все хорошо, я могу создать тип параметров статически без каких-либо проблем. Однако, когда я пытаюсь сделать то же самое с универсальными типами, я получаю неожиданные результаты:
interface Definition { optionNames?: readonly string[] }
type ConstructOptions<T1 extends Definition, T2 = T1['optionNames'][number]> = T2 extends string | number | symbol? Record<T2, string>: {}
type GenericOptions = ConstructOptions<typeof definition>
// type GenericOptions = Record<"a", string> | Record<"b", string>
Я ожидаю, что GenericOptions будет таким же, как и Options, однако в результате получается объединение записей, каждая из которых имеет только 1 из опций в качестве требуемого ключа. Почему это происходит?
Решение проблемы
Используйте кортеж, чтобы убедиться, что каждый компонент не рассматривается отдельно:
type ConstructOptions<T1 extends Definition, T2 = T1['optionNames'][number]> = [T2] extends [string | number | symbol]? Record<T2, string>: {}
Полный пример:
const definition = { optionNames: ['a', 'b'] as const }
interface Definition { optionNames?: readonly string[] }
type ConstructOptions<T1 extends Definition, T2 = T1['optionNames'][number]> = [T2] extends [string | number | symbol]? Record<T2, string>: {}
type GenericOptions = ConstructOptions<typeof definition>;
Странная вещь, о которой вы, вероятно, никогда не подумали бы, но она работает. Здесь только TypeScript ✌️.
Детская площадка
Кстати, вы можете исправить эту ошибку там с помощью NonNullableили Required;)
Комментариев нет:
Отправить комментарий