Typescript条件验证取决于字段为空

时间:2019-01-16 16:58:26

标签: typescript

我确实有以下代码:

purrr

我正在尝试验证传递给type DomainFieldDefinition<T> = { required?: boolean } type DomainDefinition<F, M> = { fields?: { [K in keyof F]: DomainFieldDefinition<F[K]> }, methods?: { [K in keyof M]: M[K] & Function }, } type User = { id: string, name?: string } export const User = createDomain<User>({ fields: { id: { required: true }, name: {}, }, }); 方法的字段的定义对象中的required键的值是否与其所基于的类型的要求相匹配(此处为{ {1}});理想情况是在编译时。

我觉得条件类型可能有助于这样做,但是我找不到基于需求的方法。具体来说,我试图将createDomain限制为:

  • User,如果该字段不可为空,
  • required或未定义

有任何提示吗?

1 个答案:

答案 0 :(得分:1)

以定义为here 的类型为例,我们可以创建一个条件类型,如果需要该字段,field类型将为{ required : true }{}类型,否则:

type DomainDefinition<F, M> = {
    fields?: {
        [K in keyof F]: ({} extends { [P in K]: F[K] } ? {} : { required: true }) & {} // Intersect with other properties as necessary
    },
    methods?: { [K in keyof M]: M[K] & Function },
}

type User = {
    id: string,
    name?: string
}

function createDomain<T>(o: DomainDefinition<T, any>) {
    return o;
}

export const User = createDomain<User>({
    fields: {
        id: { required: true },
        name: {},
    },
});

注意,这将测试可选性(?修饰符),不会测试可空性(| null | undefined),具体取决于您的用例,这可能重要也可能不重要

也可能感兴趣的this答案测试了readonly修饰符。使用它,您还可以添加一个isReadonly字段:

type IfEquals<X, Y, A, B> =
    (<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? A : B;

type DomainDefinition<F, M> = {
    fields?: {
        [K in keyof F]:
        ({} extends { [P in K]: F[P] } ? {} : { required: true })
        & IfEquals<{ [P in K]: F[P] }, { -readonly [P in K]: F[P] }, {}, { isReadonly: true }>
    },
    methods?: { [K in keyof M]: M[K] & Function },
}

type User = {
    id: string,
    readonly name?: string
}

function createDomain<T>(o: DomainDefinition<T, any>) {
    return o;
}

export const User = createDomain<User>({
    fields: {
        id: { required: true },
        name: { isReadonly: true },
    },
});

如果要过滤掉某些属性,例如函数,则必须用过滤后的F替换所有出现的F。为了使其更简单,只需定义一个额外的类型别名:

type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
type DomainPropertyHelper<F> = {
  [K in keyof F]: ({} extends { [P in K]: F[K] } ? {} : { required: true }) & {} // Intersect with other properties as necessary
}; 
type DomainDefinition<F, M> = {
    fields?: DomainPropertyHelper<Pick<F, NonFunctionPropertyNames<F>>>,
    methods?: { [K in keyof M]: M[K] & Function },
}