TypeScript 别名不会“记住”未使用的泛型类型参数或别名类型[重复]

2023-12-30

请原谅我问这个问题的冗长:

我正在使用的库(它是 redux 的抽象),称为十分简单 https://easy-peasy.dev/,公开了可以相互结合使用的以下类型。这Thunktype 有几个未使用的通用参数,它们的存在似乎只是为了提供推断thunk函数通用参数(与Thunk准确输入参数):

export type Thunk<
  Model extends object, // not used
  Payload = undefined,
  Injections = any, // not used
  StoreModel extends object = {}, // not used
  Result = any
> = {
  type: 'thunk';
  payload: Payload;
  result: Result;
};

export function thunk<
  Model extends object = {},
  Payload = undefined,
  Injections = any,
  StoreModel extends object = {},
  Result = any
>(
  thunk: (
    actions: Actions<Model>,
    payload: Payload,
    helpers: Helpers<Model, StoreModel, Injections>,
  ) => Result,
): Thunk<Model, Payload, Injections, StoreModel, Result>;

推荐的用法如下:

type Injections = {
  someFunc: () => "foo";
}

const someThunk: Thunk<LocalModel, string, Injections, StoreModel, Promise<void>> = thunk(
  (actions, payload, { injections, getState, getStoreState }) => {
     // do something
     const state = getState(); // returns local state
     const storeState = getStoreState(); // returns global / store state
     const foo = injections.someFunc(); // foo
  }
);

但是,如果您尝试创建别名Thunk类型有一个不太详细的定义,所有不被“使用”的通用参数Thunk输入自身(Model, StoreModel, and Injections),似乎迷路了,动作,注入,getState(取决于Model类型),以及getStoreState(依赖于取决于StoreModel类型)不再键入(它们变成any).

type LocalThunk<TPayload, TResult> = Thunk<
  LocalModel,
  TPayload,
  Injections,
  StoreModel,
  TResult
>;

const someThunk: LocalThunk<string, Promise<void>> = thunk(
  (actions, payload, { injections, getState, getStoreState }) => {
     // do something
     const state = getState(); // any
     const storeState = getStoreState(); // any
     const foo = injections.someFunc(); // any
  }
);

我能想到的最好的情况是,这是因为别名不会“记住”实际未在其中使用的类型Thunk type.

我有一些解决方法,所以我并不是真的在这里寻找它。我感兴趣的是是否有人可以提供更充分的理由来解释为什么会出现这种情况,以及这是否应该被视为一个错误,以及 TypeScript github 是否是提出这个问题的更好地方。

这是一个小重现,以便您可以看到它的实际效果:https://codesandbox.io/s/shy-worker-qpi5lr?file=/src/store.tsx https://codesandbox.io/s/shy-worker-qpi5lr?file=/src/store.tsx

任何支持为什么这不起作用的信息或文档都会有帮助!


这是因为 TS 可以从返回值推断出泛型,如下所示,

export function thunk<
  Model extends object = {},
  Payload = undefined,
  Injections = any,
  StoreModel extends object = {},
  Result = any
>(
  thunk: (
    actions: Actions<Model>, //Applies generic to here
    payload: Payload,
    helpers: Helpers<Model, StoreModel, Injections>, //and here
  ) => Result,
): Thunk<Model, Payload, Injections, StoreModel, Result>; //Infers the generic from the return value

但是当你封装它时就不能这样做(因为正如 jcalz 指出的那样,TS 不是名义上基于的)。

export function thunk<...>(
  thunk: (
    actions: Actions<Model>, //How do I infer this?
    payload: Payload,
    helpers: Helpers<Model, StoreModel, Injections>, //Kaboom!
  ) => Result,
): ThunkAlias<...>; //Uh oh, there is no Model value!

您必须创建一个包装函数作为解决方法。不幸的是,自从easy-peasy不导出所有实用程序类型来创建 thunk 这是不可能的

具体来说,这一行:helpers: Helpers<Model, StoreModel, Injections> https://github.com/ctrlplusb/easy-peasy/blob/3f632a06e0e8395185980ba8c7b047ed78b2ac13/index.d.ts#L564这就是尝试做到这一点时最令人头痛的原因。因为他们不出口Helpers https://github.com/ctrlplusb/easy-peasy/blob/3f632a06e0e8395185980ba8c7b047ed78b2ac13/index.d.ts#L148 type.

TS 4.7 发布了一项功能,允许您从类型化函数派生类型参数。https://github.com/microsoft/TypeScript/pull/47607 https://github.com/microsoft/TypeScript/pull/47607。我们可以使用这个模式来获取参数thunk。因此,一旦发布,或者如果您同意将 TS 的开发版本作为依赖项运行,我们就可以执行此操作。安装使用npm/yarn install/add typescript@next

type ThunkAlias<TPayload = never, TReturn = any> = Thunk<
  StoreModel,
  TPayload,
  Deps,
  never,
  TReturn
>

export function thunkAlias<
  Model extends object = StoreModel,
  Payload = undefined,
  Injections = Deps,
  SModel extends object = {},
  Result = any
>(
  args: Parameters<typeof thunk<Model, Payload, Injections, SModel, Result>>[0],
): ThunkAlias<Payload, Result> {
  return thunk<Model, Payload, Injections, SModel, Result>(args)
}

您可以在此处查看模拟的演示版本:TS游乐场 https://tsplay.dev/m3yabw

解决方法是简单地复制Helpers类型,并基于此重新定义您的包装函数。 (并导入它需要的任何其他类型)

// Yoinked from easy-peasy 
type Helpers<Model extends object, StoreModel extends object, Injections> = {
  dispatch: Dispatch<StoreModel>;
  fail: AnyFunction;
  getState: () => State<Model>;
  getStoreActions: () => Actions<StoreModel>;
  getStoreState: () => State<StoreModel>;
  injections: Injections;
  meta: Meta;
};

export function thunkAlias<
  Model extends object = StoreModel,
  Payload = undefined,
  Injections = Deps,
  SModel extends object = {},
  Result = any
>(
  _thunk: (
    actions: Actions<Model>,
    payload: Payload,
    helpers: Helpers<Model, SModel, Injections>,
  ) => Result,
): ThunkAlias<Payload, Result> {
  return thunk<Model, Payload, Injections, SModel, Result>(args)
}

附:但在某些时候,你必须问自己,为了其他地方不那么冗长,所有这些工作是否真的值得?我想这取决于你问谁...

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TypeScript 别名不会“记住”未使用的泛型类型参数或别名类型[重复] 的相关文章

随机推荐