打字稿通用和扩展
Posted
技术标签:
【中文标题】打字稿通用和扩展【英文标题】:Typescript generic and extend 【发布时间】:2020-05-13 14:34:30 【问题描述】:我是 Typescript 的新手。
我有以下 4 个接口:
export interface MyAction<T = any>
type: T
export interface MyAnyAction extends MyAction
[extraProps: string]: any
export interface MyAnyAnyAction extends MyAnyAction
export interface ITestDispatch<A extends MyAction = MyAnyAction>
<T extends A>(action: T): T
我想创建一个“ITestDispatch”类型的函数。
我不明白为什么 TS 编译器会为以下函数抛出错误:
const TestDispatch1Func: ITestDispatch1<MyAnyAction> = (action: MyAnyAnyAction): MyAnyAnyAction =>
let obj: MyAnyAnyAction =
type: 'skdw',
da: 20
;
return obj;
我在“TestDispatch1Func”上收到以下错误:
Type '(action: MyAnyAnyAction) => MyAnyAnyAction' is not assignable to type 'ITestDispatch<MyAnyAction>'. Type 'MyAnyAnyAction' is not assignable to type 'T'. 'MyAnyAnyAction' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'MyAnyAction'
.
感谢您解决我的疑问。
【问题讨论】:
<T extends A>(action: T): T
意味着您的函数必须返回与其参数相同的 A
子类型。所以你的函数接受一个参数action
,它可能是MyAnyAction
的子类型,并且必须返回相同的子类型;但它实际上只返回基本类型MyAnyAction
。
@kaya3 我已经修改了我的问题。请您看一下并告诉我修改后的示例是否满足您的论点的要求?谢谢。
你可以说它没有,因为 Typescript 仍然会给你类型错误。函数类型<T extends A>(action: T): T
仅由返回与其参数相同类型的函数满足,该类型可以是A
的任何子类型。您修改后的函数完全忽略了其参数action
的类型,并且只返回MyAnyAction
的一个硬编码子类型,因此它不能保证返回与action
相同的子类型。
【参考方案1】:
这实际上是您的函数签名中的一个模糊错误。 <MyAnyAction>
声明了一个新的类型参数,它改变了 MyAnyAction 的含义。很难发现,因为类型参数与代码中的接口同名。
const TestDispatch1Func: ITestDispatch<MyAnyAction> =
<MyAnyAction>(action: MyAnyAction): MyAnyAction =>
应该是
const TestDispatch1Func: ITestDispatch<MyAnyAction> =
(action: MyAnyAction): MyAnyAction =>
对于更多上下文,您可以将<MyAnyAction>
重命名为任何内容,因为它是一个类型参数,在此上下文中,它意味着创建一个名为 MyAnyAction 的类型参数。如果你重命名是那么错误也更清楚:
const TestDispatch1Func: ITestDispatch<MyAnyAction> = <T>(action: T): T =>
类型“MyAnyAction”不可分配给类型“T”。 'MyAnyAction' 是 可分配给“T”类型的约束,但“T”可以是 使用不同子类型的约束“”进行实例化
【讨论】:
谢谢。但是,如果我使用上述构造,那么我会收到以下错误:类型“(操作:MyAnyAction)=> MyAnyAction”不可分配给类型“ITestDispatch1ITestDispatch
成为T
中的通用函数。如果您真的想让它通用,这将起作用:typescriptlang.org/play/#code/…。但是你不能创建 T
类型的对象文字,因为 T
可以是任何东西
@sudip 上述构造的要点是您会收到一个错误,但消息会显示您的上下文中的 MyAnyAction 是一个类型参数。【参考方案2】:
这是因为接口ITestDispatch
表示该函数可以执行A
的任何子类型的动作,因此它与函数TestDispatch1Func
的类型声明和返回类型冲突,仅限于MyAnyAnyAction
.接口接受 A 的任何子类型,而实现只接受 1 个子类型。
例如,如果你有另一个接口
export interface AnotherAction extends MyAnyAction
ITestDispatch1<MyAnyAction>
的定义允许您调用 TestDispatch1Func(action: AnotherAction)
,因为 AnotherAction extends MyAnyAction
,但这显然会与仅期望 MyAnyAnyAction
的函数定义冲突。
这里有3个解决方案
export interface MyAction<T = any>
type: T
export interface MyAnyAction extends MyAction
[extraProps: string]: any
export interface MyAnyAnyAction extends MyAnyAction
// solution 1: specify the action input & output types in function defnition
export interface ITestDispatch1<T extends MyAction = MyAnyAction>
(action: T): T
// this means that the function will always be called with MyAnyAnyAction and return MyAnyAnyAction
const TestDispatchFunc1: ITestDispatch1<MyAnyAnyAction> = (action) =>
// here you can always return `MyAnyAnyAction`,
// because you explicitly declared it the as function output type
let obj: MyAnyAnyAction =
type: 'skdw',
da: 20
;
return obj;
// solution 2: separate action input & output types, specify output type in function defintion
export interface ITestDispatch2<A extends MyAction, R extends A>
<T extends A>(action: T): R
// this function can be called with any subtype of MyAnyAction, but will always return MyAnyAnyAction
const TestDispatchFunc2: ITestDispatch2<MyAnyAction, MyAnyAnyAction> = (action) =>
// here you can always return `MyAnyAnyAction`,
// because you explicitly declared it the as function output type
let obj: MyAnyAnyAction =
type: 'skdw',
da: 20
;
return action;
// solution 3: decide the function input & output types types in function invocation
export interface ITestDispatch3<A extends MyAction = MyAnyAction>
<T extends A>(action: T): T
// this function can be called with any subtype of MyAnyAction, returns the same subtype
const TestDispatchFunc3: ITestDispatch3<MyAnyAction> = (action) =>
// can't return MyAnyAnyAction because the return type is the same as the input type,
// which can be any subtype of MyAnyAction, not necessarily MyAnyAnyAction
// let obj: MyAnyAnyAction =
// type: 'skdw',
// da: 30
// ;
return action;
// the result type is determined base on the input type
const result1 = TestDispatchFunc3( as MyAnyAnyAction) // result: MyAnyAnyAction
const result2 = TestDispatchFunc3( as MyAnyAction) // result: MyAnyAction
TS Playground here
【讨论】:
以上是关于打字稿通用和扩展的主要内容,如果未能解决你的问题,请参考以下文章