Typescript:Intellisense 自动完成 - 自定义函数作为深度嵌套对象 属性,从对象 属性 推断它的参数类型

Typescript: Intellisense autocomplete - custom function as deeply nested object property, that infers it's argument type from the object property

基本上我有这种类型

type X = {
  a: {
    b?: string;
    c?: string;
    d: {
      e:{
        f:{
          g?: boolean
        }
      }
    };
  };
};

// and a helper
const as = <T,>(arg: T):T => arg;

而且我需要能够创建一个 X 形状的对象 其中所有嵌套级别上的所有属性都替换为:

as({
    // autocomplete here with actual object property
    // unless it's not an object
})

// like so

const x = as<X>({
  a: as({
    b: as("some string"),
    d: as({
      e: as({
        f: as({
          g: as(false)
        })
      })
    })
  })
})

我在一定程度上让它工作了,但是在超过 2 层的嵌套中,自动完成不再...自动完成

请参阅Typescript playground link以更好地理解问题。

所以...在来自 typescript 的 Discord 社区的人的帮助下,我让它工作了。

最终工作示例:

type IsObject<T> = T extends Array<any>
    ? false
    : T extends Date
    ? false
    : T extends object
    ? true
    : false;

type BuiltInFn = "someFn";

type NullableUndefinedOrEmptyUnion = "nullable" | "undefined" | "emptyString";

type Primitive<T> = T extends string
    ? "string"
    : T extends boolean
    ? "boolean"
    : T extends number
    ? "number"
    : never;

type NN<T> = NonNullable<T>;

type S<T, R = T> = IsObject<NN<T>> extends true
    ? {
          [K in keyof T]?: IsObject<NN<T[K]>> extends false
              ? S<NN<T[K]>, R>
              :
                    | (<XX extends S<NN<T[K]>, R>>(
                          value: XX | undefined,
                          root: R
                      ) => boolean | void)
                    | (<XX extends S<NN<T[K]>, R>>(
                          value: <YY extends S<NN<T[K]>, R>>(value: YY, root: R) => boolean | void,
                          root: R
                      ) => XX | void);
      }
    :
          | (<XX extends NN<T>>(
                value: Primitive<XX> extends never
                    ? BuiltInFn | undefined
                    : Primitive<XX> | undefined,
                root: R
            ) => XX | void)
          | (<XX extends NN<T>>(
                value: <YY extends NN<T>>(value: YY, root: R) => boolean | void,
                root: R
            ) => XX | void);

function s<T>(s: S<T, T>): S<T, T> {
    return s;
}

type A<T> = T extends (value: infer U, root: infer UU) => infer Z ? U : never;

function as<T>(arg: A<T>, ...opts: Array<BuiltInFn | NullableUndefinedOrEmptyUnion>): NN<T> {
    return arg as any;
}

type X = {
    a?: {
        b?: string;
        c?: number;
        d: {
            nested?: {
                e: {
                    f: boolean;
                };
                g: {
                    x: number;
                };
            };
        };
    };
};

const x = s<X>({
    a: as({
        d: as({
            nested: as({
                e: as({
                    f: as("boolean"),
                }),
            }),
        }),
        b: as("string"),
    }),
});

现在您可以使用可以接收 3 种类型作为参数的自定义函数:

  • 对象 属性 本身(如果 prop === 对象)带有可选参数
  • 一个带两个参数的匿名函数(对象 属性 本身和根对象)
  • 如果参数不是对象,则为表示参数类型的字符串文字,或如上所述的匿名函数

而且...无论多深,所有内容都已输入

漂亮!