函数参数的Typescript泛型约束

时间:2017-10-04 07:51:40

标签: typescript

编辑: 固定"测试"到"约束"在接口

我无法理解通用约束在Typescript中的工作方式。我正在使用React / Redux,在那里你可以找到类似于这个的结构:

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

如您所见,这是一个带有泛型参数和一些约束的函数定义。现在,当我想使用它时,我可以写下以下内容:

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

哪个好用,但是当我想扩展测试界面时(让我们称之为TestWithData):

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

我收到编译时错误:

错误TS2322:输入&#39;(s:string,c:TestWithData)=&gt;空隙&#39;不能分配类型&#39; SomeType&#39;。   参数类型&#39; c&#39;和&#39;行动&#39;是不相容的。     输入&#39; A&#39;不能分配给&#39; TestWithData&#39;。       输入&#39; SomeConstraint&#39;不能分配给&#39; TestWithData&#39;。         财产&#39;测试&#39;类型&#39; SomeConstraint&#39;

中缺少

我缺少什么?

被修改

正如我所说,我在redux类型中找到了这个(类似的)构造。你可以找到这个定义(redux&#34; 3.7.2&#34;):

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

现在,当你想要定义你的reducer你需要提供状态和动作时,我会跳过状态部分,所以,我可以写这样的动作:

interface MyAction { type: "MY_ACTION" }

并且很容易创建reducer:

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

将通过编译,但如果我向MyAction添加其他数据如下:

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

编译将因上述错误而失败。我怀疑它会通过。那么这个约束的目的是什么?告诉编译器我们期望完全相同的类型(结构上)而不是更多或更少?我认为这是一个非常有限的用例,我希望类型推断会选择类型,检查它的结构兼容性并保留类型签名。这样的东西,但不需要指定类型参数:

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

现在,保留了类型签名,但我们需要在声明变量时指定实际的参数类型(与我们对状态的操作相同):

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

1 个答案:

答案 0 :(得分:1)

对于您给出的示例,我不认为泛型+约束是我定义类型的方式(它可能是我在标称类型系统中的方式,但不是在结构类型系统中。)

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

如果您说&#34;该项目必须具有constraint属性,您可以使用更简单的方式获取此项:

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

结构化打字负责其余部分。

在实际实现中,您可以使用与SomeType定义兼容的任何类型......

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

}

以下是完整版,请记住Test界面需要constraint属性,或者扩展SomeConstraint界面,否则您将错过基本使类型兼容的属性。

interface SomeConstraint {
    constraint: any
}

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

interface Test extends SomeConstraint{ test: string }

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

}

const a: Test = { constraint: '', test: '' };

actualImplementation('', a);

上述示例中的关键点是实际实现可以是您的Test类型,没有错误。