联合判别中是否可能使用递归数组类型?

时间:2019-12-26 11:03:13

标签: arrays typescript discriminated-union union-types

我刚刚开始发现TypeScript,并且正在测试限制。我正在寻找的是一种使接口作为字段的方法,具体取决于其中一个属性的值。

例如:

type type RecursiveArray<T> = T | RecursiveArray<T>[];
type allowedTypesString = 'string' | 'email' | 'date' | 'id' | 'number' | 'boolean' | 'object';


interface IOptions {
    type: RecursiveArray<allowedTypesString>,
    required?: boolean,
    defaultValue?: PROVIDED_TYPE,
    expected?: PROVIDED_TYPE[],
    transform?: (value: PROVIDED_TYPE) => any,
    validate?: (value: PROVIDED_TYPE) => boolean,
    regexp?: RegExp,
    min?: number,
    max?: number,
    params?: object,
}

我希望IOptions具有:

    仅当regex是“ string”或“ email”时,
  • type属性。
  • params仅在type是“对象”的情况下。
  • minmax仅在type是“数字等...

我看到我可以像在this线程中那样使用区分联合,但是正如您所看到的,type属性是RecursiveArray<allowedTypesString>,这意味着它可以是单个字符串字符串数组字符串数组 ...

有了工会,我可以宣布:

interface IOptionsString {
    type: string,
    defaultValue?: string,
    expected?: string[],
    regexp?: RegExp,
}
如果type是一个字符串,将调用

。但是,如果我收到一个字符串数组或一个字符串数组怎么办?

我正在做什么?否则,我将只处理单个数组,但是我想知道我在想什么在TypeScript中是可能的。

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

如果使用实际类型对IOptions进行参数化,然后将其转换为常见类型和依赖于该类型的特殊类型的交集,则可以使用条件类型来做您想做的事情。

要返回原始类型(例如您的示例中的PROVIDED_TYPE),您需要将allowedTypesString更改为从字符串类型到实际类型的映射,然后使用条件类型进行验证(即从值)。

type RecursiveArray<T> = T | RecursiveArray<T>[];

interface TypeStringMap {
  'string': string;
  'email': string;
  'date': Date;
  'id': string;
  'number': number;
  'boolean': boolean;
  'object': object;
}

type RecursiveTypes = RecursiveArray<keyof TypeStringMap>;

type HasRegexp<T extends RecursiveTypes> =
  T extends RecursiveArray<'string' | 'email'> ? { regexp: RegExp } : {}

type HasMinMax<T extends RecursiveTypes> =
  T extends RecursiveArray<'number'> ? { min: number, max: number } : {}

type ReifyRecursiveType<T> =
  T extends keyof TypeStringMap ? TypeStringMap[T]
  : (T extends (infer U)[] ? ReifyRecursiveType<U>[] : never)

type IOptions<T extends RecursiveTypes> = {
  type: T;
  expected?: ReifyRecursiveType<T>[];
  defaultValue?: ReifyRecursiveType<T>,
  transform?: <TResult>(value: ReifyRecursiveType<T>) => TResult,
  validate?: (value: ReifyRecursiveType<T>) => boolean,
} & HasRegexp<T> & HasMinMax<T>

type IStringOptions = IOptions<'string'>; // has `regexp` field
type IStringOrEmailOptions = IOptions<('string' | 'email')>; // has `regexp` field
type IEmailArrayArrayOptions = IOptions<'email'[][]>; // has `regexp` field
type INumberOptions = IOptions<'number'>; // has `min` and `max` fields