简体   繁体   English

函数参数的打字稿通用约束

[英]Typescript generic constraint for function parameter

edited: fixed "test" to "constraint" in interfaces编辑:将界面中的“测试”固定为“约束”

I am having trouble understanding how generic constraints are working in Typescript.我无法理解通用约束在 Typescript 中是如何工作的。 I am working with React/Redux where you can find construction similar to this one:我正在使用 React/Redux,您可以在其中找到与此类似的构造:

interface SomeConstraint { constraint: any }
type SomeType<T> = <A extends SomeConstraint>(item: T, action: A) => void;

As you can see, this is a function definition with generic parameters and some constraint.如您所见,这是一个带有通用参数和一些约束的函数定义。 Now, when I want to use it, I can write the following:现在,当我想使用它时,我可以编写以下内容:

interface Test { constraint: string }
const some : SomeType<string> = (s: string, c: Test ) => {}

Which works great, but then, when I want to extend the test interface (let's call it TestWithData):这很好用,但是,当我想扩展测试接口时(我们称之为 TestWithData):

interface TestWithData  { constraint: string, payload: any }
const some : SomeType<string> = (s: string, c: TestWithData ) => {}

I get a compile time error:我收到编译时错误:

error TS2322: Type '(s: string, c: TestWithData) => void' is not assignable to type 'SomeType'.错误 TS2322:类型 '(s: string, c: TestWithData) => void' 不可分配给类型 'SomeType'。 Types of parameters 'c' and 'action' are incompatible.参数“c”和“action”的类型不兼容。 Type 'A' is not assignable to type 'TestWithData'.类型“A”不可分配给类型“TestWithData”。 Type 'SomeConstraint' is not assignable to type 'TestWithData'.类型“SomeConstraint”不可分配给类型“TestWithData”。 Property 'test' is missing in type 'SomeConstraint'. “SomeConstraint”类型中缺少属性“test”。

What I am missing?我缺少什么?


Edited已编辑

As I said I found this (similar) construct in redux typings.正如我所说,我在 redux 类型中发现了这个(类似的)构造。 You can find this definition (redux "3.7.2"):你可以找到这个定义(redux "3.7.2"):

export type Reducer<S> = <A extends Action>(state: S, action: A) => S;

Now when you want to define your reducer you need to provide state and action, I'll skip the state part, so, I can write action to something like this:现在当你想定义你的减速器时,你需要提供状态和动作,我将跳过状态部分,所以,我可以写这样的动作:

interface MyAction { type: "MY_ACTION" }

and creating reducer is easy:创建减速器很容易:

const reducer: Reducer<MyState> = (state: MyState, action: MyAction) => {...}

which will pass compilation, but if I add additional data to MyAction as following:这将通过编译,但如果我向 MyAction 添加其他数据,如下所示:

interface MyAction { 
    type: "MY_ACTION";
    payload: any
}

compilation will fail with the mentioned error.编译将因上述错误而失败。 I was suspecting that it would pass.我怀疑它会通过。 So what is the purpose for this constraint?那么这个约束的目的是什么? To tell compiler that we expect exactly the same type (structurally) and nothing more or less?告诉编译器我们期望完全相同的类型(结构上)并且仅此而已? I think that this is a very limited use case, I would expect that the type inference would pick the type, check it's structural compatibility and preserve the type signature.我认为这是一个非常有限的用例,我希望类型推断会选择类型,检查它的结构兼容性并保留类型签名。 Something like this but without need to specify the type argument:像这样但不需要指定类型参数:

export type Reducer<S, A extends Action> = (state: S, action: A) => S;

Now, type signature is preserved, but we need to specify the actual parameter type when declaring a variable (same as we do for state):现在,类型签名被保留,但我们需要在声明变量时指定实际参数类型(与我们对状态所做的相同):

const reducer: Reducer<MyState, MyAction> = (state: MyState, action: MyAction) => {...}

For the example you have given, I don't think generic + constraint is the way I would define the type (it probably would be how I would do it in a nominal type system, but not in a structural type system).对于你给的例子,我不认为通用+约束是我定义的类型(它很可能是我会怎么做它标称的类型系统,而不是在一个结构类型系统)的方式。

type SomeType<T> = <A extends SomeConstraint>(item: T, action: A) => void;

If you are saying "the item must have a constraint property, you can get this using the simpler:如果您说“该项目必须具有constraint属性,则可以使用更简单的方法来获得它:

type SomeType<T> = (item: T, action: SomeConstraint) => void;

Structural typing takes care of the rest.结构类型负责其余的工作。

Here is my updated version as the previous answer stopped working in TypeScript 3.8.3.这是我的更新版本,因为之前的答案在 TypeScript 3.8.3 中停止工作。 I have made the constraint generic as the constraint will be the same type as the item.我已将约束设为通用,因为约束将与项目具有相同的类型。 Because the parameter types can be inferred, we don't need to state them explicitly (ie we don't need to write string three times... like this const actualImplementation: SomeType<string> = (item: string, action: string): void => { } - we can just write it once as the type argument.因为可以推断参数类型,所以我们不需要显式声明它们(即我们不需要写三次string ......像这样const actualImplementation: SomeType<string> = (item: string, action: string): void => { } - 我们可以只写一次作为类型参数。

interface SomeConstraint<T> {
    constraint: T
}

type SomeType<T> = (item: T, action: SomeConstraint<T>) => void;

const actualImplementation: SomeType<string> = (item, action): void => { }

const a: SomeConstraint<string> = { constraint: '' };

actualImplementation('', a);

The key point in the above example is that the actual implementation can be your Test type with no errors.上面示例中的关键点是实际实现可以是您的Test类型,没有错误。

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

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