繁体   English   中英

Typescript中的映射对象类型

[英]Mapped object types in Typescript

这是一个普通的打字稿问题,但我将根据目前正在使用的react-redux应用程序进行说明。 如有必要,我可以举一个一般的例子,但希望现在就足够了。 我正在尝试将redux-thunk反应应用程序中的actionCreators映射到已经分配了dispatch和getState的类型中。 我有:

export interface AppThunkAction<TAction= {}, TDispatchResponse = void, TActionResponse = void> {
     (dispatch: (action: TAction) => TDispatchResponse, getState: () => ApplicationState): TActionResponse;
}

我的动作创作者看起来像这样:

@typeName('BAR_ACTION')
export class BarAction extends Action {
    constructor() {
        super();
    }
}

@typeName('FOO_ACTION')
export class FooAction extends Action {
    constructor(public baz: string) {
        super();
    }
}

export const actionCreators = {
    foo: (baz: string): AppThunkAction<FooAction, void, Promise<void>> => (dispatch, getState): Promise<void> => {
        return new Promise((resolve, reject) =>
            resolve(dispatch(new FooAction(baz)))
        );
    },
    bar: (): AppThunkAction<BarAction, void, Promise<number>> => (dispatch, getState): Promise<number> => {
        return new Promise((resolve, reject) => {
            dispatch(new BarAction());
            resolve(3);
        });
    },
};

这些对象由redux-thunk中间件处理,因此实际类型(我需要为组件指定)看起来像这样

export type actionCreatorsTypes = {
    foo: (baz: string) => Promise<void>,
    bar: () => Promise<number>,
};

问题是我必须手动编写这些类型定义,如果我忘记更改它们,打错字等,事情就会变得混乱。

因此,我正在寻找自动化的方式来处理此问题。 一个函数或修饰器,它将在此类型上进行迭代并进行修改,就像注入了dispatch和getState一样。 我已经看过打字稿中的映射类型,但到目前为止,我仍在努力创建比简单选择或简单映射更复杂的东西。

有人对如何解决这个问题有暗示吗?

原始答案,2017年10月

从版本2.5开始,TypeScript不支持使用函数类型进行类型操作:

  • 缺少可变参数类型意味着您无法对不同数量的参数的函数进行概括。 在您的情况下,没有一种简单的方法可以写出foo属性将接受单个string参数,而bar属性则不接受参数,从而与泛型很好地映射。

  • 您也不能采用任意函数类型并以编程方式提取其参数或返回类型,就像使用keyof和lookup类型使用对象属性keyof 有一个建议可以为您提供这种针对函数类型的功能,但目前尚不存在。

  • 最后,您不能以没有将诸如映射条件类型之类的东西将AppThunkAction<T, D, R>R的方式来任意解开类型

每种方法都有解决方法,但是它们比仅以您现在的方式手动指定函数类型更丑陋/笨拙/维护性较差。 我已经沿着那条路线走了好几次,很少值得这么做。

因此,这意味着从概念上讲简单的操作“采用任何函数类型(a:A, b:B, ...)=>AppThunkAction<T, D, R>并产生相关函数类型(a: A, b: B, ...)=>R “不能真正完成。 抱歉。 🙁


我确实知道手动指定类型是多余的,但是如果您进行一些测试代码,则在更改类型时至少应该捕获错误:

if (false as true) {
  // errors will appear in here if you leave out keys or change signatures
  const act: actionCreatorsTypes = {
    foo: (x) => actionCreators.foo(x)(null!, null!),
    bar: () => actionCreators.bar()(null!, null!)
  };
}

只是一个想法。 祝好运!


更新,2018年9月

好吧,由于提出了这个问题,所以引入了一些关键功能,这些功能从“您不能真正做到”变为“您可以很容易做到”:

  • TypeScript 2.8 使用infer关键字为我们提供了条件类型类型推断 ,使我们能够从函数签名中拉出参数并返回类型,并将AppThunkAction<T, D, R>R

  • TypeScript 3.0使我们能够在rest / spread表达式中使用元组,以一种通用的方式表示“函数参数列表”的类型。 这是支持可变参数类型的很大一部分,对于我们这里的目的来说已经足够了。

这些覆盖了原始答案中缺少的功能。 让我们使用它们! 这是我们的方法:

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

type UnthunkedActionCreators<O extends Record<keyof O, (...args: any[]) => AppThunkAction<any, any, any>>> = {
  [K in keyof O]: UnthunkedActionCreator<O[K]>
}

UnthunkedActionCreator将返回AppThunkAction的函数转换为具有相同参数的函数,该函数返回AppThunkAction返回的内容。 UnthunkedActionCreators地图UnthunkedActionCreator过的对象。 让我们看看它是否有效:

type ActionCreatorsType = UnthunkedActionCreators<typeof actionCreators>;

检查为

// type ActionCreatorsType = {
//   foo: (baz: string) => Promise<void>;
//   bar: () => Promise<number>;
// }

看起来不错。 看,我们要做的就是等待一年。 干杯!

现在可以实现(从3.0版开始),下面是代码:

type GetActionCreatorType<FF extends (...args: any[]) => (...args: any[]) => any> =
  FF extends (...args: infer A) => (...args: infer _) => infer R ? (...args: A) => R 
  : never;

type GetActionCreatorsTypes<T extends {[key: string]: (...args: any[]) => (...args: 
  any[]) => any}> = { 
    [P in keyof T]: GetActionCreatorType<T[P]>; 
};

您可以在单个功能上使用第一个功能,而第二个功能可以在动作创建者的对象上使用。

我的答案是基于这个答案

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM