如何使用类型参数调用 const 函数?

How to call const function with type arguments?

根据 redux-form typings 的定义:

export type DataSelector<FormData = {}, State = {}> = (formName: string, getFormState?: GetFormState) => (state: State) => FormData;

export const getFormValues: DataSelector;

请注意 DataSelector 有两个类型参数。如果我调用 getFormValues,这是一个 const 函数,我该如何提供类型参数?

type FormFields = { name: string };


// No type argument specified so: Property 'name' does not exist on type '{}'.
const name = getFormValues('my-form')(state)?.name;


// I could cast it, but no thank you ...
const name = (getFormValues('my-form')(state) as any as FormFields)?.name;


// Need to supply `FormFields` as the generic argument for `FormData`, but how? This doesn't work ...
const name = getFormValues<FormFields>('my-form')(state)?.name;
  1. 我能否以某种方式为 const 函数指定通用参数?
  2. 打字有误吗?如何纠正它们?

更新: 这种情况下的输入不正确。而不是...

export type DataSelector<FormData = {}, State = {}> = (formName: string, getFormState?: GetFormState) => (state: State) => FormData;

……应该是……

export type DataSelector = <FormData = {}, State = {}>(formName: string, getFormState?: GetFormState) => (state: State) => FormData;

...(= 右侧的通用参数)经过此更改,现在有效:

const name = getFormValues<FormFields>('my-form')(state)?.name;

已打开 PR:https://github.com/DefinitelyTyped/DefinitelyTyped/pull/49896

这里的问题是 getFormValues 不是通用函数,因此没有直接的方法可以将其用于您的目的。类型 DataSelectorDataSelector<{}, {}> 相同,因为 DataSelector 的定义使用 type parameter defaults。所以 getFormValues 本质上有以下类型:

declare const getFormValues: (
  formName: string, getFormState?: GetFormState
) => (state: {}) => {};

里面没有通用的东西了。如果你调用 getFormValues() 它 return 是一个 (state: {}) => {} 类型的函数,它接受(几乎)任何东西和 returns {},一个没有已知属性的对象类型.


我不太确定 DataSelector 的预期用途是什么,也不确定 getFormValues() 是如何实现的,所以以下只是我可能用来获得适合您的内容的类型具体示例代码。

如果您希望 getFormValues 自身 成为泛型函数,则需要将泛型类型参数从 DataSelector type 声明和 call signature,像这样:

type GenericDataSelector = <FormData = {}, State = {}>(
  formName: string, getFormState?: GetFormState
) => (state: State) => FormData;

然后让 getFormValues 成为 的值 类型:

const getFormValuesGeneric = getFormValues as GenericDataSelector;

这会给你你想要的行为,我认为:

const nameOkay = getFormValuesGeneric<FormFields>('my-form')(state).name;

这行得通,尽管我对没有类型参数推理站点的泛型函数有点怀疑。 GenericDataSelector 类型的调用签名在两个参数中是通用的,这两个参数都没有出现在调用签名的参数中...它们只出现在 return 类型中。

这意味着如果我调用 getFormValuesGeneric("something"),编译器不知道如何推断 StateFormData 应该是什么,它最终默认为 {}{}。如果你想要不同的东西,你需要手动指定它,比如 getFormValuesGeneric<MyFormData, MyState>("something").

这行得通,但你最终会说 getFormValuesGeneric<FormData1, State1>("something") return 是一个 (state: State1) => FormData1 类型的函数,而 getFormValuesGeneric<FormData2, State2>("something") return 是一个类型的函数(state: State2) => FormData2 类型的函数。但是当然在运行时类型系统是 erased,所以这两个都是 getFormValuesGeneric("something")。两个相同的调用如何 return 两种不同的函数类型取决于已被删除的内容?他们不能,真的。

意思是至多 <FormData1, State1><FormData2, State2>之一是getFormValuesGeneric("something")的正确选择...并选择正确的我想,是编写调用该函数的 TS 代码的人的责任。但是在那种情况下,这与使用 type assertion (您所谓的“演员”)之间没有太大区别;他们都不保证任何类型的安全。

据推测,传入的 formName 参数的值与 FormDataState 的正确规范之间存在某种关系,但这对编译器来说是未知的。可以想象重写类型,以便编译器能够跟踪此映射,但我认为这超出了这个问题的范围。

从所有这些中得出的结论是 在使用其参数无法从任何东西合理推断出的泛型函数时要小心


Playground link to code