typescript 类型推导的问题,有没有办法让下面这种 store 对象参数形式也支持类型自动推导?

能正常推导的例子

function createStore<
  R,
  E extends Record<string, (arg: R) => void>
>(reducers: R, effects: E) {}

createStore(
  {
    hello() {},
  },
  {
    world(arg) {
      // 这里能自动推导 arg: { hello(): void }
      arg.hello();
    },
  },
);

我封装库需要写成以下参数形式

function createStore<
  R,
  E extends Record<string, (arg: R) => void>
>(store: { reducers: R; effects: E }) {}

createStore({
  reducers: {
    hello() {},
  },
  effects: {
    world(arg) {
      // 这里无法推导 TS2571: Object is of type 'unknown'.
      arg.hello();
    },
  },
}); 

有没有办法让下面这种 store 对象参数形式也支持类型自动推导?

阅读 3.5k
4 个回答

解决了

function createStore<R, E extends Record<string, (arg: R) => void>>(store: {
  reducers: R;
  effects: E;
}) {}

createStore({
  reducers: {
    hello: () => {},
//  ^^^^^^^^^^^^^^^^
  },
  effects: {
    world(arg) {
      arg.hello();
    },
  },
});

这里必须是箭头函数,并且函数的参数必须表明类型。这算 ts 的 bug 吗

更新回答

可以使用 建造者模式

https://stackoverflow.com/que...

可能有难度,函数调用的时候,正常情况下,函数调用的时候是根据参数来推导类型,并跟声明的类型比对。如果参数类型比较简单,可以反向推导给出提示……不过你这个结构,已经是两层类型了,好像不能简单地反向推导用来提示。

可以手工指定第一项的类型,然后接下来就可以推导了

function createStore<
  R,
  E extends Record<string, (arg: R) => void>
>(store: { reducers: R; effects: E }) {}

createStore({
    reducers: {
        hello() { },
    } as { [key: string]: () => void },
//    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 指定类型
    effects: {
        world(arg) {
            arg.hello();
        },
    },
});

还可以简化一下 createStone 的类型参数,然后在调用前指定类型

function createStore<R>(store: {
//                  ^^^
    reducers: R;
    effects: Record<string, (arg: R) => void>
//           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}) { }

createStore<{ [key: string]: () => void }>({
//         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    reducers: {
        hello() { },
    },
    effects: {
        world(arg) {
            arg.hello();
        },
    },
});

只能部分达到你要的效果,并不能完全自动化。

楼上说的对,本来是平级的可以推导,现在变成了两个孙子、还不是一个爹,TS 自己推导不出来了。

不过你完全可以自己写声明,而不是仅仅依赖推导。

// .d.ts
type Effects<R> = Record<string, (arg: R) => void>;
type Store<R> = { reducers: R; effects: Effects<R> };
function createStore<R>(store: Store<R>) {}

// .ts
type ThisReducers = { hello: () => void; };
createStore<ThisReducers>({
    reducers: {
        hello() {},
    },
    effects: {
        world(arg) {
            arg.hello();
        },
    },
});
function createStore<
  R extends Record<string, (...argv: unknown[]) => void>,
  E extends Record<string, (arg: R) => void>,
> (store: { reducers: R; effects: E }) {}

createStore({
  reducers: {
    hello() {},
  },
  effects: {
    world(arg) {
      arg.hello();
    },
  },
});
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
logo
Microsoft
子站问答
访问
宣传栏