如何定义与级别1相同但在打字稿中具有不同休息级别的对象类型?

时间:2018-10-26 12:05:34

标签: typescript

我有2个对象,它们是相同的道具,但是不同的道具结果。

const tom = {
    getAction: () => ({
        eat: () => {},
        sleep: () => {},
    }),
}

const steve = {
    getAction: () => ({
        work: () => {}
    }),
}

我的func希望将2个对象和额外的getAction函数结合到父级

const mapObjIndexed = <K extends string, T, R>(fn: (value: T) => R, data: Record<K,T>) => {
    return Object.keys(data).reduce((previous, key) => ({
        ...previous,
        [key]: fn(data[key])
    }),{})
}
interface Person<T> {
    getAction: () => T
}
const combineAction = <G, K extends string, T extends Person<G>>(people: Record<K, T>) => {
    return mapObjIndexed((item) => item.getAction(), people)
}
const actions = combineAction({tom, steve})

// So I can do like this
actions.tom.eat() // problems at tom
actions.tom.sleep() // same

actions.steve.work() // same

这里有2个问题:

第一:如何定义mapObjectIndexed的返回类型

2nd:打字稿如何知道汤姆道具包含饮食功能和史蒂夫工作原理

谢谢帮助

1 个答案:

答案 0 :(得分:0)

根据您指定的参数类型,mapObjIndexed的自然返回类型为Record<K, R>。 (您必须将返回的表达式强制转换为Record<K, R>,因为TypeScript不够聪明,无法看到您正在逐个添加所有必需的属性。)

但是,这对于combineAction用例没有用,因为combineAction输入中的每个人都有一个不同类型T,而您想要输出的相应属性具有相应的类型R。您可以使用映射类型如下表示combineAction的类型:

const combineAction = <P extends { [N in keyof P]: Person<{}> }>(people: P):
    { [N in keyof P]: ReturnType<P[N]["getAction"]> } => {
    return <any>mapObjIndexed((item) => item.getAction(), people)
}

但是,没有一种好的方法来声明mapObjIndexed的类型,该类型将允许您通过对combineAction的调用而无需强制转换来实现mapObjIndexed,因为您实际上必须参数化在原始属性类型mapObjIndexed与转换后的属性类型P[N]之间的关系ReturnType<P[N]["getAction"]>。为此,mapObjIndexed必须具有一个higher-kinded type,尽管存在一些有限的解决方法,但TypeScript目前不支持该an example。如果您遇到这种情况,我可能会声明combineAction的类型,而不用担心实现中的类型转换。