如何在 类 上实现类型?
how can I implement types on classes?
我有这个基本的 class:
type Action = { type: string; payload?: {} }
type Dispatch<ActionType extends Action> = (action: ActionType) => void
type Actions =
| { type: '@ADD', payload: { item: string, id: string } }
| { type: '@REM', payload: { id: string } }
class CardActions implements ActionClass<Actions> { // this isn't working as I expect, see below
constructor(private dispatch: Dispatch<Actions>) {}
disp(action) {} // this isn't working
}
尝试实现一个接口对构造函数和 disp
函数都不起作用,它总是有错误:
Index signature is missing in type 'CardsActions'
或
Class 'CardActions' incorrectly implements interface 'ActionClass'.
export interface ActionClass<ActionType extends Action> {
// new(dispatch: string): any;
[actionKey: string]: (action: ActionType['type']) => void
}
我希望它像任何功能一样工作,我误解了吗?
预期
interface Func {
(action: Actions['type']): void
}
const func: Func = (action) => {} // this will work
为什么他们不像彼此一样工作?我怎样才能让它发挥作用?
对于这个答案,我将使用略微修改的 ActionClass
版本,它删除了泛型,因为它们与您 运行 遇到的特定问题无关:
interface ActionClass {
[actionKey: string]: (action: Actions['type']) => void
}
TypeScript 的一个当前限制是,当 class
实现 interface
时,编译器不使用 contextually type the members of the class. In class Foo implements Bar {...}
, the compiler will infer a type for Foo
without consulting Bar
at all; and only afterward will it check that type against Bar
. See microsoft/TypeScript#32082 的接口定义以及其中的许多相关问题阅读更多相关信息。
目前,如果您想让 class Foo implements Bar {...}
工作,您将需要手动显式地为 Foo
的成员提供编译器所需的一切,以便 Foo
的推断类型可分配给 Bar
.
现在 CardActions
被推断为就好像你这样写:
class CardActions {
constructor(private dispatch: Dispatch<Actions>) { }
disp(action) { }
}
因为你没有给CardActions
一个index signature,编译器不会推断出一个。因此 CardActions
不被视为可分配给 ActionClass
,它确实有一个:
class CardActions implements ActionClass { // error
constructor(private dispatch: Dispatch<Actions>) { }
disp(action) { }
}
另外,disp()
的action
参数被推断为any
,因为编译器根本没有参考ActionClass
,而你没有注释action
.
因此,这里修复的第一步是为 CardActions
提供显式索引签名,并为 disp()
的 action
参数提供显式类型:
class CardActions implements ActionClass {
constructor(private dispatch: Dispatch<Actions>) { } // error
[actionKey: string]: (action: Actions['type']) => void;
disp(action: Actions['type']) { }
}
现在我们有一个新的错误。
注意 ActionClass
有一个字符串索引签名。这意味着 ActionClass
对象的 所有 属性必须是 (action: Actions['type']) => void
类型的函数。但是在您的构造函数中,您已将 private
关键字放在 dispatch
参数上,使其成为 parameter property。那是 shorthand 类似的东西:
class CardActions implements ActionClass {
private dispatch: Dispatch<Actions>; // error
constructor(dispatch: Dispatch<Actions>) {
this.dispatch = dispatch;
}
[actionKey: string]: (action: Actions['type']) => void;
disp(action: Actions['type']) { }
}
这就是你的问题。 dispatch
属性 是 Dispatch<Actions>
类型,但索引签名要求每个 属性 都可以分配给 (action: Actions['type']) => void
。并且 Dispatch<Actions>
不可分配给那个(参数类型错误)。
我不确定解决此问题的正确方法是什么,因为我不确定您是真的想要书面的索引签名还是真的想要 dispatch
属性。但是你不可能两者都没有错误。让我们删除 dispatch
属性 以查看错误是否消失:
class CardActions implements ActionClass {
constructor(dispatch: Dispatch<Actions>) { }
[actionKey: string]: (action: Actions['type']) => void;
disp(action: Actions['type']) { }
}
万岁,没有错误!可能有一百种不同的可能方法可以代替这个,但探索这些可能超出了这个问题的范围。
好了。如果你想让 class 实现一个带有字符串索引签名的接口,你需要在 class 定义中显式声明索引签名,并且你需要确保每个已知的 属性 的 class 与索引签名兼容。
我有这个基本的 class:
type Action = { type: string; payload?: {} }
type Dispatch<ActionType extends Action> = (action: ActionType) => void
type Actions =
| { type: '@ADD', payload: { item: string, id: string } }
| { type: '@REM', payload: { id: string } }
class CardActions implements ActionClass<Actions> { // this isn't working as I expect, see below
constructor(private dispatch: Dispatch<Actions>) {}
disp(action) {} // this isn't working
}
尝试实现一个接口对构造函数和 disp
函数都不起作用,它总是有错误:
Index signature is missing in type 'CardsActions'
或
Class 'CardActions' incorrectly implements interface 'ActionClass'.
export interface ActionClass<ActionType extends Action> {
// new(dispatch: string): any;
[actionKey: string]: (action: ActionType['type']) => void
}
我希望它像任何功能一样工作,我误解了吗?
预期
interface Func {
(action: Actions['type']): void
}
const func: Func = (action) => {} // this will work
为什么他们不像彼此一样工作?我怎样才能让它发挥作用?
对于这个答案,我将使用略微修改的 ActionClass
版本,它删除了泛型,因为它们与您 运行 遇到的特定问题无关:
interface ActionClass {
[actionKey: string]: (action: Actions['type']) => void
}
TypeScript 的一个当前限制是,当 class
实现 interface
时,编译器不使用 contextually type the members of the class. In class Foo implements Bar {...}
, the compiler will infer a type for Foo
without consulting Bar
at all; and only afterward will it check that type against Bar
. See microsoft/TypeScript#32082 的接口定义以及其中的许多相关问题阅读更多相关信息。
目前,如果您想让 class Foo implements Bar {...}
工作,您将需要手动显式地为 Foo
的成员提供编译器所需的一切,以便 Foo
的推断类型可分配给 Bar
.
现在 CardActions
被推断为就好像你这样写:
class CardActions {
constructor(private dispatch: Dispatch<Actions>) { }
disp(action) { }
}
因为你没有给CardActions
一个index signature,编译器不会推断出一个。因此 CardActions
不被视为可分配给 ActionClass
,它确实有一个:
class CardActions implements ActionClass { // error
constructor(private dispatch: Dispatch<Actions>) { }
disp(action) { }
}
另外,disp()
的action
参数被推断为any
,因为编译器根本没有参考ActionClass
,而你没有注释action
.
因此,这里修复的第一步是为 CardActions
提供显式索引签名,并为 disp()
的 action
参数提供显式类型:
class CardActions implements ActionClass {
constructor(private dispatch: Dispatch<Actions>) { } // error
[actionKey: string]: (action: Actions['type']) => void;
disp(action: Actions['type']) { }
}
现在我们有一个新的错误。
注意 ActionClass
有一个字符串索引签名。这意味着 ActionClass
对象的 所有 属性必须是 (action: Actions['type']) => void
类型的函数。但是在您的构造函数中,您已将 private
关键字放在 dispatch
参数上,使其成为 parameter property。那是 shorthand 类似的东西:
class CardActions implements ActionClass {
private dispatch: Dispatch<Actions>; // error
constructor(dispatch: Dispatch<Actions>) {
this.dispatch = dispatch;
}
[actionKey: string]: (action: Actions['type']) => void;
disp(action: Actions['type']) { }
}
这就是你的问题。 dispatch
属性 是 Dispatch<Actions>
类型,但索引签名要求每个 属性 都可以分配给 (action: Actions['type']) => void
。并且 Dispatch<Actions>
不可分配给那个(参数类型错误)。
我不确定解决此问题的正确方法是什么,因为我不确定您是真的想要书面的索引签名还是真的想要 dispatch
属性。但是你不可能两者都没有错误。让我们删除 dispatch
属性 以查看错误是否消失:
class CardActions implements ActionClass {
constructor(dispatch: Dispatch<Actions>) { }
[actionKey: string]: (action: Actions['type']) => void;
disp(action: Actions['type']) { }
}
万岁,没有错误!可能有一百种不同的可能方法可以代替这个,但探索这些可能超出了这个问题的范围。
好了。如果你想让 class 实现一个带有字符串索引签名的接口,你需要在 class 定义中显式声明索引签名,并且你需要确保每个已知的 属性 的 class 与索引签名兼容。