基于输入到函数的键数组的动态返回类型

问题描述 投票:0回答:1

我的 AWS lambda 函数中使用了 x 个模块,例如

UserModule
NotificationsModule
CompanyModule
等。我创建了一个接口,描述模块的结构如下:

interface Modules {
    company: ICompanyModule
    user: {
        base: IUserModule
    }
    notifications: {
        settings: INotificationSettingsModule
        details: INotificationDetailsModule
    }
}

假设我需要在一个函数中使用公司和用户库模块,我必须手动导入并实例化它们。如果我只有少数几个功能就可以工作,但事实并非如此。为了减少导入和手动工作量,我认为我应该创建一个

factory
方法,该方法将返回一个仅包含该函数所需模块的对象。例如:

// Single module
const module = factory('company') // Type would be ICompanyModule

// Multiple Modules
const modules = factory(['company', 'user.base']) // Type would be { company: ICompanyModule, 'user.base': IUserModule } 

工厂方法的输入将是

Modules
界面中的键数组。

我即将找到“解决方案”,但我似乎无法修复

Type instantiation is excessively deep and possibly infinite
错误。以下是我目前拥有的:

// This gets all the keys and nested keys of my Modules interface
type Path<T, Key extends keyof T = keyof T> = Key extends string
    ? T[Key] extends Record<string, unknown>
        ?
            | `${Key}.${Path<T[Key], Exclude<keyof T[Key], keyof Array<unknown>>> & string}`
            | `${Key}.${Exclude<keyof T[Key], keyof Array<unknown>> & string}`
            | Key
        : Key
    : never

// This gets the value for the Path wether it be nested or not
type PathValue<T, P extends Path<T>> = P extends `${infer Key}.${infer Rest}`
    ? Key extends keyof T
        ? Rest extends Path<T[Key]>
            ? PathValue<T[Key], Rest>
            : never
        : never
    : P extends keyof T
        ? T[P]
        : never

// This is my ReturnType that takes in the a single key or array of keys and builds the object type
type ReturnObject<T extends Object, P extends Path<T> | Array<Path<T>>> = P extends Array<Path<T>>
    ? {
        [Key in P[number]]: Key extends keyof T
            ? T[Key] extends Object
                ? Key extends Path<T[Key]>
                    ? ReturnObject<T[Key], Key>
                    : T[Key]
                : never
            : Key extends `${infer K}.${infer Rest}`
                ? K extends keyof T
                    ? T[K] extends Object
                        ? Rest extends Path<T[K]>
                            ? ReturnObject<T[K], Rest>
                            : never
                        : never
                    : never
                : never
    }
    : P extends keyof T
        ? T[P]
        : P extends `${infer K}.${infer Rest}`
            ? K extends keyof T
                ? T[K] extends Object
                    ? Rest extends Path<T[K]>
                        ? ReturnObject<T[K], Rest>
                        : never
                    : T[K]
                : never
            : never

// This is the factory function declaration
declare function factory<T extends ModuleFactory, P extends Path<T> | Array<Path<T>>>(
    modules: P
): ReturnObject<T, P>

现在可以了。当我使用任何给定的键创建调用

factory
时,我会得到正确的对象结构,但 Typescript 在
Type instantiation is excessively deep and possibly infinite
 处的 
ReturnType
 中抛出 
{ [Key in P[number]]: ... }

错误

经过几天的混乱,我最好的猜测是问题是 P 可以扩展数组>并且 Typescript 正在尝试处理传入的数组可能具有巨大长度并进而导致几乎无限递归的情况.

typescript generics recursion type-inference keyof
1个回答
0
投票

您可以使用不易出错的路径获取器

Get
来获取值和泛型类型验证器
conforms
以仅允许正确的路径

https://tsplay.dev/mqyVZm

type conforms<T, V> = T extends V ? T : V;

type validatePath<Path, Obj> =
  | Path extends keyof Obj ? Path
  : Path extends `${infer F extends keyof Obj & string}.${infer L}` ? `${F}.${validatePath<L, Obj[F]>}`
  : keyof Obj & string

type Get<Obj, Path> =
  | Path extends `${infer F}.${infer L}` ? Get<Get<Obj, F>, L> 
  : Path extends keyof Obj ? Obj[Path]
  : [Obj, Path]

function factoryS<Path extends string>(path: conforms<Path, validatePath<Path, Modules>>): Get<Modules, Path> {
  return null!;
}
function factoryA<const A>(list: conforms<A, { [K in keyof A]: validatePath<A[K], Modules> }>)
  : A extends readonly string[] ? { [K in A[number]]: Get<Modules, K> } : never {
  return null!;
}
// you may make them single function overloads
© www.soinside.com 2019 - 2024. All rights reserved.