typescript - io-ts:使用字面量联合,在对象中定义必需和可选键
我正在尝试使用 io-ts 定义一个新的编解码器。
完成后形状应如下所示:
type General = unknown;
type SupportedEnv = 'required' | 'optional'
type Supported = {
required: General;
optional?: General;
}
(注意:目前General的形状并不重要)
这里的关键是我想根据General和SupportedEnv推导类型。
目前,我有类似这样的东西:
const GeneralCodec = iots.unknown;
const SupportedEnvCodec = iots.union([
iots.literal('required'),
iots.literal('optional'),
]);
const SupportedCodec = iots.record(SupportedEnvCodec, GeneralCodec)
type Supported = iots.TypeOf<typeof SupportedCodec>;
类型 Supported 具有 两个必需的键:
type Supported = {
required: General;
optional: General;
}
我如何才能使 optional 真正可选?
我尝试过使用交集和部分类型……但我无法弄清楚 iots.record 的语法。
这是可能的吗?我需要以不同的方式思考这个问题吗?
答案#1
更新
创建一个映射函数 partialRecord(利用 io-ts 对 fp-ts 的同伴依赖),我们可以达到我们一直想要的目标。
import { map } from 'fp-ts/Record';
import * as iots from 'io-ts';
export const partialRecord = <K extends string, T extends iots.Any>(
k: iots.KeyofType<Record<string, unknown>>,
type: T,
): iots.PartialC<Record<K, T>> => iots.partial(map(() => type)(k.keys));
const GeneralCodec = iots.unknown;
const SupportedEnvCodec = iots.keyof({
required:null,
optional:null,
});
type SupportedEnv = iots.TypeOf<typeof SupportedEnvCodec>;
type RequiredEnv = Extract<SupportedEnv, 'required'>;
const RequiredEnvCodec: iots.Type<RequiredEnv> = iots.literal('required');
type OtherEnvs = Exclude<SupportedEnv, RequiredEnv>;
const OtherEnvsCodec: iots.KeyofType<Record<OtherEnvs, unknown>> = iots.keyof({optional:null});
const OtherEnvsRecordCodec = partialRecord<
OtherEnvs,
typeof GeneralCodec
>(OtherEnvsCodec, GeneralCodec);
const SupportedCodec = iots.intersection([
iots.record(RequiredEnvCodec, GeneralCodec),
OtherEnvsRecordCodec,
]);
type Supported = iots.TypeOf<typeof SupportedCodec>;
这产生了所需的类型:
type Supported = {
required: unknown;
} & {
optional?: unknown;
}
原始答案
我最接近的方法是添加一个额外的间接级别,这很不幸。
例如:
const GeneralCodec = iots.unknown;
const SupportedEnvCodec = iots.union([
iots.literal('required'),
iots.literal('optional'),
]);
type SupportedEnv = iots.TypeOf<typeof SupportedEnvCodec>;
type RequiredEnv = Extract<SupportedEnv, 'required'>;
const RequiredEnvCodec: iots.Type<RequiredEnv> = iots.literal('required');
type OtherEnvs = Exclude<SupportedEnv, RequiredEnv>;
const OtherEnvsCodec: iots.Type<OtherEnvs> = iots.union([
iots.literal('optional'),
]);
const SupportedCodec = iots.intersection([
iots.record(RequiredEnvCodec, GeneralCodec),
iots.partial({
env: iots.record(OtherEnvsCodec, GeneralCodec),
}),
]);
type Supported = iots.TypeOf<typeof SupportedCodec>;
这会产生以下类型:
type Supported = {
required: unknown;
} & {
env?: {
optional: unknown;
} | undefined;
}
哪个……接近——尽管我真的希望我不需要那额外的层,但它确实以一种迂回的方式回答了我最初的问题。
