打字稿中的递归类型映射

时间:2020-08-11 22:14:08

标签: typescript typescript-generics

我正在尝试从包含本机类型验证功能的对象中提取类型映射,例如,如果我有该对象

const test  = {
  Test: {
    Test1: {
      Test2: SimpleStringValidator //return type is string or undefined but input can be anything
    },
  }
}

我要提取类型

type Extracted = {
  Test: {
    Test1: {
      Test2: string
    }
  }
}

为此,我编写了以下代码以提取包含一组嵌套验证函数的对象的返回类型

Sample.ts


export type Validator<T> = NativeTypeValidator<T> | ObjectValidator<T>

export type NativeTypeValidator<T> = (n: any) => T | undefined
export type ObjectValidator<O> = {
  [K in keyof O]: Validator<O[K]> 
}

//native validators
export const SimpleStringValidator:NativeTypeValidator<string> = (val) => typeof(val) === "string" ? val : undefined

//object validator function
export const ObjValidator = <V>(validatorObj: ObjectValidator<V>) => (o:any):V =>{
  let result = {} as V;
  //we can only validate objects
  if (typeof (o) !== "object") { return undefined; }
  const validatorKeys = Object.keys(o) as [keyof ObjectValidator<V>]
  validatorKeys.forEach((validatorKey) => { 
    const objValue = o[validatorKey] as V[keyof V];
    const objectValidator = validatorObj[validatorKey]
    if (!objectValidator) { return undefined } //do nothing if no validator exists for the key in o
    //figure out if we have a nested object validator or a native validator at the corresponding key of validatorObj
    if (typeof (objectValidator) === "object") {
      result[validatorKey] = ObjValidator(objectValidator as ObjectValidator<V[keyof V]>)(objValue)
    }
    else {
      const nativeValidator = objectValidator as NativeTypeValidator<V[keyof V]>;
      result[validatorKey] = nativeValidator(objValue)
    }
  })
  return result;
}

export const test  = {
  Test: {
    Test1: {
      Test2: SimpleStringValidator
    },
  }
}

export const validatorFunc = ObjValidator(test);
export const outputExample = validatorFunc({
  Test: {
    Test1: {
      Test2: "hi"
    },
  }
})

outputExample.Test.Test1.Test2 = "1";
outputExample.Test.Test1.Test2 = 1; //vs code intellisense complains because needs to be type string

在我的情况下,intellisense自动完成嵌套属性Test2并将其类型设置为字符串,但是当我创建类型声明文件时,类型信息不同,因此无法将此生成的类型正确导出到其他项目。具体来说,它将属性Test1的类型设置为任何。我要生成的类型是上面代码中的validateatorFunc的输出和outputExample的类型。

生成的sample.d.ts

export declare type Validator<T> = NativeTypeValidator<T> | ObjectValidator<T>;
export declare type NativeTypeValidator<T> = (n: any) => T | undefined;
export declare type ObjectValidator<O> = {
    [K in keyof O]: Validator<O[K]>;
};
export declare const SimpleStringValidator: NativeTypeValidator<string>;
export declare const ObjValidator: <V>(validatorObj: ObjectValidator<V>) => (o: any) => V;
export declare const test: {
    Test: {
        Test1: {
            Test2: NativeTypeValidator<string>;
        };
    };
};
export declare const validatorFunc: (o: any) => {
    Test: {
        Test1: any;
    };
};
export declare const outputExample: {
    Test: {
        Test1: any;
    };
};

我需要使用根验证器对象作为提取类型的源,以寻找生成的声明文件具有正确类型的解决方案。我在Typescript操场上放了这段代码,它显示了自动完成功能与.d.ts文件中内容之间的区别。

TypescriptPlayground example

1 个答案:

答案 0 :(得分:1)

如果要提取:

{
  Test: {
    Test1: {
      Test2: string
    }
  }
}

您可以使用infer使用此递归类型映射:

type ValidatedObject<T> = Partial<{
  [key in keyof T]: T[key] extends ObjectValidator<infer Type>
    ? ValidatedObject<Type>
    : T[key] extends NativeTypeValidator<infer Type>
    ? Type
    : T[key] extends object
    ? ValidatedObject<T[key]>
    : T[key];
}>

Playground

注意:部分存在是有两个原因,一个是类型完全输出到 .d.ts 文件,另一个是undefined返回值。

>