Javascript 我应该如何在Typescript中定义一个动态类型,它应该保留用户输入的实际类型

Javascript 我应该如何在Typescript中定义一个动态类型,它应该保留用户输入的实际类型,javascript,typescript,types,typescript-typings,Javascript,Typescript,Types,Typescript Typings,我正在创建一个函数,它接受param中的某个对象,接口如下{[someKey]:(data:any)=>any} type GenericRecord<value> = Record<string, value> function createFunctionObject (funcObject: GenericRecord<(someParam: GenericRecord<any>, randomKey: string) => any>

我正在创建一个函数,它接受param中的某个对象,接口如下
{[someKey]:(data:any)=>any}

type GenericRecord<value> = Record<string, value>

function createFunctionObject (funcObject: GenericRecord<(someParam: GenericRecord<any>, randomKey: string) => any>) {
  return Object.fromEntries(
    Object.entries(funcObject).map(([key, fuc]) => [
      key,
      (param: GenericRecord<any>) => fuc(param[randomKey])
    ])
  );
}
这意味着我将得到3个函数作为返回
{x,y,z}
,使用
x,y,na
解构将抛出一个错误。此外,它应该保留x将返回一个数字,y,z将返回一个字符串。但是我得到的
createFunctionObject
的返回类型签名是
{[k:string]:(state:genericord)=>any;}

功能示例:
function createFunctionObject(funcObject,randomKey){
返回Object.fromEntries(
Object.entries(funcObject.map)([key,fuc])=>[
钥匙
(参数)=>fuc(参数[随机键])
])
);
}
const{a,b,c}=createFunctionObject({a:(arg)=>arg.a,b:(arg)=>arg.b,c:(arg)=>arg.c},'x')
log(a({x:{a:20}}))//20

log(b({x:{b:30}))//30
首先,让我们描述一下
createFunctionObject
在类型级别应该做什么,而不必担心实现。给定类型为
T
的输入
funcObject
和类型为
K
的输入
randomKey
,其中
T
表示其值为一个参数函数的对象,
K
表示
字符串
createFunctionObject
的输出类型为
FunctionObject
,定义如下:

type FunctionObject<T extends Record<keyof T, (arg: any) => any>, K extends string> =
    { [P in keyof T]:
        T[P] extends (arg: infer A) => infer R ?
        (arg: { [Q in K]: A }) => R : never
    };

现在,虽然我们可以在类型级别表达此转换,但实际上不可能让编译器验证您的实现是否符合此类型签名。
Object.fromEntries()
Object.entries()
Array.prototype.map()
的标准库类型不够详细,因此即使我们为它们添加了自己的类型签名,我们也会发现编译器无法以避免编译器错误的方式组合它们。所以,让我们不要尝试这样做

相反,我们将告诉编译器不要过于担心验证实现。实现这一点的一种方法是在实施过程中:

function createFunctionObject<
  T extends Record<keyof T, (arg: any) => any>, 
  K extends string
>(
    funcObject: T,
    randomKey: K
): FunctionObject<T, K> {
    return Object.fromEntries(
        Object.entries(funcObject).map(([key, fuc]) => [
            key,
            (param: Record<string, any>) => (fuc as Function)(param[randomKey])
        ])  // assert here
    ) as any; // assert here
}
注意,我必须将
a
b
c
回调参数注释为
{a:number}
{b:number}
{c:number}
;如果未将它们标记为
a=>arg.a
,编译器将不知道如何推断它们,这将返回到
any
。这是,所以最好在编译器无法推断的情况下进行注释

无论如何,您可以看到结果函数是可调用的,并且具有正确的类型签名:

/* const a: (arg: {
    randomKey: {
        a: number;
    };
}) => number */
console.log(a({ randomKey: { a: 20 } })) // 20

/* const b: (arg: {
    randomKey: {
        b: number;
    };
}) => number */
console.log(b({ randomKey: { b: 30 } })) // 30
看起来不错


是否适用于您的用例?请注意,如果在
arg
上没有某种类型的类型注释,就不能只编写
(arg)=>arg.a
,除非您希望它的类型是
any
。。。我写了
(arg:{a:number})=>arg.a
,因为您似乎就是这样使用它的。此外,我无法判断您是否希望在其输入的
a
x
属性上调用输出函数,因为您的示例既有
fuc(param.a)
又有
fuc(param.x)
。请考虑将这里的代码修改为A,如果能满足您的需要,我会很乐意写一个答案。@ jCalz您的解决方案是完美的,<代码> t[k]扩展(ARG:推断A)=推断R?(arg:{x:A})=>R:never完成了所有的技巧。另外,我已经更新了代码段,可以对用户提供的任何随机键和对象调用输出函数。@jcalz,还有一个问题,在不同的声明中定义签名与在定义函数时定义签名有何不同。因为当我这样做时,返回的对象中会出现不兼容ok键的错误。据我所知,这就是我们重载签名的方式
function createFunctionObject<
  T extends Record<keyof T, (arg: any) => any>, 
  K extends string
>(
    funcObject: T,
    randomKey: K
): FunctionObject<T, K> {
    return Object.fromEntries(
        Object.entries(funcObject).map(([key, fuc]) => [
            key,
            (param: Record<string, any>) => (fuc as Function)(param[randomKey])
        ])  // assert here
    ) as any; // assert here
}
// call signature
function createFunctionObject<
  T extends Record<keyof T, (arg: any) => any>, 
  K extends string
>(
    funcObject: T,
    randomKey: K
): FunctionObject<T, K>;

// implementation (checked more loosely)
function createFunctionObject(
  funcObject: Record<string, Function>, 
  randomKey: string
) {
    return Object.fromEntries(
        Object.entries(funcObject).map(([key, fuc]) => [
            key,
            (param: Record<string, any>) => fuc(param[randomKey])
        ])
    );
}
const { a, b, c } = createFunctionObject({
    a: (arg: { a: number }) => arg.a,
    b: (arg: { b: number }) => arg.b,
    c: (arg: { c: number }) => arg.c
}, "randomKey")
/* const a: (arg: {
    randomKey: {
        a: number;
    };
}) => number */
console.log(a({ randomKey: { a: 20 } })) // 20

/* const b: (arg: {
    randomKey: {
        b: number;
    };
}) => number */
console.log(b({ randomKey: { b: 30 } })) // 30