Зависимые непересекающиеся союзы в Typescript

Учитывая простой Record<string, Record<string, any>>, определенный как постоянная запись возможных именованных конфигураций, Мне нужен тип, который динамически ограничивает тип конфигурации на основе ключа. Рассмотрим следующий пример:

const configs = {
    "fooConfig": {c: "s"},
    "barConfig": {c: 1, b: 2}    
} as const;

type ConfigDef<k extends keyof typeof configs = keyof typeof configs> = {
    a: k,
    v: typeof configs[k]
}

const x: ConfigDef = {
    a: "fooConfig",
    v: {c: 1, b: 2} // this should not work
}

Важно отметить, что для типа ConfigDef мне нужно, чтобы пользователь мог использовать этот тип неявно, не передавая фактический ключ конфигурации в качестве универсального типа (т.е. ему не нужно использовать явный синтаксис const x: ConfigDef<"fooConfig">). Возможно ли это, и если да, то как?

Вы хотите, чтобы тип configs.fooConfig.c был string или просто s? То же самое для другой конфигурации: это буквальный тип 1 и 2 или number?

Jared Smith 06.05.2022 17:14

Вы можете сделать это с помощью универсальной вспомогательной функции, которая выведет за вас универсальные шаблоны в ConfigDef.

catgirlkelly 06.05.2022 17:15

Обратите внимание, что k — это общий параметр тип, и поэтому он обычно пишется в верхнем регистре как K, чтобы отличить его от потенциального ценность (например, переменной с именем k).

jcalz 06.05.2022 17:20
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
3
27
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если вы хотите ссылаться на ConfigDef как на конкретный (не универсальный) тип, то он должен оцениваться как союз вашего исходного ConfigDeg<K> для каждого K в keyof typeof configs. То есть нужно распространятьConfigDef<K> по союзу keyof typeof configs. Один из способов сделать это — написать тип объекта дистрибутива, как это было придумано в Майкрософт/TypeScript#47109:

type ConfigDef = { [K in keyof typeof configs]: {
    a: K,
    v: typeof configs[K]
} }[keyof typeof configs]

Это оценивает

/* type ConfigDef = {
    a: "fooConfig";
    v: {
        readonly c: "s";
    };
} | {
    a: "barConfig";
    v: {
        readonly c: 1;
        readonly b: 2;
    };
} */

и поэтому дает вам поведение, которое вы хотите:

const x: ConfigDef = {
    a: "fooConfig",
    v: { c: 1, b: 2 } // error!
}

Тип дистрибутивного объекта — это когда вы сразу индексировать в получаете отображаемый тип. Общая форма — {[K in KS]: F<K>}[KS], где KS — ключевой тип. В вашем случае KS является keyof typeof configs и F<K> вашим исходным ConfigDef<K>.

Ссылка на код для игровой площадки

Другие вопросы по теме