打字稿通用相关

时间:2020-01-29 14:17:32

标签: typescript generics

最近进入打字稿世界,看到了与泛型有关的困惑。我在下面看到了一些代码。我知道“调度”之后的<>是描述“调度”的类型,但是为什么有?

它是否充当描述“动作”中使用的T的额外类型信息?非常感谢

enter image description here

2 个答案:

答案 0 :(得分:1)

export interface Dispatch<A extends Action = AnyAction> {
    <T extends A>(action: T): T
}

此声明为call signature。等效的声明为:

export type Dispatch<A extends Action = AnyAction> =
    <T extends A>(action: T) => T

这又可以在TypeScript文档中看到:Generic Types

因此,这意味着:

任何类型为Dispatch<A>的函数(其中A为自由类型变量,其具体类型应扩展为Action,默认值为AnyAction)将接收该类型的任何值({{1} })扩展T并返回相同类型的值(可能是其自身)。

类型声明(此处为A)中的类型参数与函数类型声明(此处为A(此处)之间的重要区别在于,前者可让您指定函数实现的特定子类型或调用者应该期望它是,而后者意味着该函数应该接受任何满足约束的类型,并且调用者可以假定它可以接受任何这种类型。

答案 1 :(得分:1)

让我们检查一下这个界面:

interface Dispatch<A extends Action = AnyAction> {
    <T extends A>(action: T): T;
}

首先,接口DispatchA中是通用的,因此您可以指定A从中获取具体类型。指定的类型Aconstrained,可以分配给Action,如果不指定类型,它将defaultAnyAction

let actionDispatcher: Dispatch<Action>; //okay
let anyActionDispatcher: Dispatch; // Dispatch<AnyAction>
let badDispatcher: Dispatch<string>; // error!
// not an Action ---------> ~~~~~~

interface FooAction extends Action {
    foo: string;
}

let fooDispatcher: Dispatch<FooAction>; // okay

因此,一旦指定了A,就可以通过将A的所有实例替换为指定的类型来获得具体的类型。让我们看一下Dispatch<FooAction>。你得到这个:

// same type as Dispatch<FooAction>
interface DispatchFooAction {
    <T extends FooAction>(action: T): T;
}

所以,现在的问题是:什么是DispatchFooAction

好,该接口只有一个call signature,因此可以用作函数。我们来看一下签名:<T extends FooAction>(action: T): T

这是一个generic function签名,其类型参数T必须可分配给FooAction(或A中指定的任何Dispatch<A>);它采用T类型的参数,并返回相同类型的值。


让我们看看是否可以实现并使用Dispatch<FooAction>

// good implementation
fooDispatcher = <T extends FooAction>(a: T) => a; // okay

const x = fooDispatcher({ type: "foo", foo: "" }); // okay, 
// T inferred as { type: string; foo: string; }, x is of that type

const y = fooDispatcher({ type: "foo", foo: "", baz: 1 }); // okay, 
// T inferred as { type: string; foo: string; baz: number; }, y is of that type

const z = fooDispatcher({ type: "foo" }); // error!
// missing foo prop --> ~~~~~~~~~~~~~~~

interface BarAction extends FooAction {
    bar: string;
}

// bad implementation
fooDispatcher = <T extends BarAction>(a: T) => a; // error!
//~~~~~~~~~~~ <-- bar missing in FooAction but required in BarAction

良好的实现有效; fooDispatcher是有效的Dispatch<FooAction>,因为它是一个接受FooAction或其任何子类型的输入并返回相同类型的函数。您可以看到xy正确调用了该函数,但是z没有正确调用该函数,因为输入不是有效的FooAction

错误的执行不起作用; <T extends BarAction>(a: T)=>a仅声明接受BarAction输入,但是fooDispatcher必须接受任何 FooAction,并非每个FooAction都是BarAction


因此,希望这对您有所帮助。祝你好运!

Playground link to code