如何通用键入不一致功能的修改后的字典

时间:2019-10-23 21:09:26

标签: typescript generics

我有一个函数,需要将具有部分动态属性的函数字典转换为具有类似属性的新函数字典。

到目前为止我所拥有的:

LivyClientBuilder livy = new LivyClientBuilder();   livy.setURI(new URI(livyUrl));
    livy.setConf(LivyConstant.NUM_EXECUTOR, String.valueOf(livyProperty.getNumExecutors()));
    livy.setConf(LivyConstant.DRIVER_MEMORY, livyProperty.getDriverMemory());
    livy.setConf(LivyConstant.EXECUTOR_MEMORY, livyProperty.getExecutorMemory());
    livy.setConf(LivyConstant.QUEUE, livyProperty.getQueue());


    return livy.build();

我要完成的工作:

interface Dictionary<T extends any> {
    [key: string]: T
}

type InputFunction<T> = (value: T, parameters?: any) => T

type InputFunctionMap<T> = Dictionary<InputFunction<T>>

type OutputFunction = (parameters?: any) => void

type OutputFunctionMap<T, U extends InputFunctionMap<T>> = {
    [P in keyof U]: OutputFunction
}

function create<T>(key: string, value: T): [() => T, (value: T) => void]
function create<T, U = InputFunctionMap<T>>(key: string, value: T, inputs: U): [() => T, Readonly<OutputFunctionMap<T, U>>]
function create<T, U = InputFunctionMap<T>>(key: string, value: T, inputs?: U): [() => T, (value: T) => void] | [() => T, Readonly<OutputFunctionMap<T, U>>] {
    // ...
}

问题是我无法弄清楚如何正确地和泛型地键入const [getFoo, updateFoo] = create('foo', 'FOO') getFoo() // => 'FOO' updateFoo('foo') // => 'foo' getFoo() // => 'foo' interface MathParameters { operation: 'MULTIPLY' | 'DIVIDE' operand: number } const [getBar, {addToBar, addSevenToBar, applyMathToBar}] = create('bar', 0, { addToBar: (value, parameters: number) => value + parameters, addSevenToBar: (value) => value + 7, applyMathToBar: (value, parameters: MathParameters) => { switch (parameters.operation) { case 'MULTIPLY': return value * parameters.operand default: return value } } }) getBar() // => 0 addSevenToBar() getBar() // => 7 addToBar(3) getBar() // => 10 applyMathToBar({operation: 'MULTIPLY', operand: 2}) getBar() // => 20 (我不希望它们成为parameters),以便可以从输入函数中推断出类型并可以正确用作输出函数中的参数。

更新

谢谢您的支持!

我只是无法使输出映射按预期工作:Typescript Playground

any

抛出:type TailParams<T> = T extends (arg: any, ...args: infer A) => void ? A : never const input = inputs[name as keyof U] outputs[name as keyof U] = (...args: TailParams<typeof input>) => { // }

1 个答案:

答案 0 :(得分:2)

我可能会这样输入create()

type TailFunc<T> = T extends (h: any, ...args: infer A) => infer R ? (...args: A) => R : never;

declare function create<T>(key: string, value: T): [() => T, (value: T) => void]
declare function create<T, U extends Record<keyof U, (value: T, ...args: any) => any>>(
    key: string,
    value: T,
    inputs: U
): [() => T, { [K in keyof U]: TailFunc<U[K]> }]

TailFunc<T>类型采用函数类型T,并返回已删除第一个参数的新函数。因此TailFunc<(a: string, b: number, c: boolean) => string>成为(b: number, c: boolean) => string

create()的第二个呼叫签名照常在TU(这是inputs参数的类型)中通用。这被限制为Record<keyof U, (value: T, ...args: any)=>any,因此它期望U是对象类型,其值是所有函数的第一个参数为T类型。

该呼叫签名的返回类型是一个元组,其中第二个元素是对TailFunc的所有属性执行U的映射类型。

让我们确保它能起作用:

getBar() 
addSevenToBar()
getBar() 
addToBar(3)
getBar() 
applyMathToBar({ operation: 'MULTIPLY', operand: 2 })
getBar() 

所有这些都不会在类型系统中给出任何错误,因此类型就是您想要的。我猜它是否在运行时表现出预期,取决于create()的实现。

好的,希望能有所帮助;祝你好运!

Link to code


请注意,如何实现create(),以使所有使用类型断言最少的类型检查都不在原始问题的范围内,因此,我仅发布一种链接来实现此目的:{{ 3}}