打字稿通用和扩展
Typescript generic and extend
我是 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'
.
感谢您解开我的疑惑。
这实际上是您函数签名中的一个隐蔽错误。 <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 => {
Type 'MyAnyAction' is not assignable to type 'T'. 'MyAnyAction' is
assignable to the constraint of type 'T', but 'T' could be
instantiated with a different subtype of constraint '{}'
这是因为接口ITestDispatch
意味着该函数可以执行A
的任何子类型的操作,所以它与函数的类型声明和return类型冲突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
我是 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'
.
感谢您解开我的疑惑。
这实际上是您函数签名中的一个隐蔽错误。 <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 => {
Type 'MyAnyAction' is not assignable to type 'T'. 'MyAnyAction' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'
这是因为接口ITestDispatch
意味着该函数可以执行A
的任何子类型的操作,所以它与函数的类型声明和return类型冲突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