【问题标题】:how can I implement types on classes?如何在类上实现类型?
【发布时间】:2023-03-08 00:46:01
【问题描述】:

我有这个基础课:


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函数都不起作用,它总是有错误:

“CardsActions”类型中缺少索引签名

“CardActions”类错误地实现了“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

playground

为什么他们不能像彼此一样工作?我怎样才能让它发挥作用?

【问题讨论】:

  • 您真的希望字符串索引签名和调用签名表现相同吗?也就是说,您是否希望能够像调用函数一样调用CardActions 的实例?如果不是,您能否详细说明为什么您认为它们应该相同?如果您删除问题中以“我希望它像任何功能一样工作,我误解了吗?”开头的所有内容,我可以回答为什么它不起作用。但是我不确定将类或类实例比作函数会得到什么。
  • 你也有// new(dispatch: string): any;在那里,被注释掉了。你在做什么?为什么它是你问题的一部分?如果您在其中注释它将不起作用,因为在class Foo implements Bar {} 中,Bar 类型对应于Fooinstances,而不是Foo 构造函数本身。 Foo 的实例将不可构造,因此您不希望 Bar 中的构造签名。如果没有ms/TS#33892 之类的东西,就无法使用implements 来约束类的静态方面。
  • 我已经回答了这个问题,好像// new(dispatch: string): any; 和以I'm expecting it to work like any function 开头的所有内容都不存在。如果您需要其他内容,请修改问题并详细说明这些要点。祝你好运!
  • 谢谢你的回答,我只是出于期望,为什么我们没有类方法作为函数,不是每次我们声明一个新方法时我们都必须添加类型,并且我更喜欢定义类型的方法,然后在必要时进行调整,这就是我所说的函数示例,这是打字稿的限制吗?
  • 是的,这是一个限制;请参阅下面我的答案以及“一个当前限制”开头的部分,以及链接的 GitHub 问题。

标签: javascript typescript types typescript-typings


【解决方案1】:

对于这个答案,我将使用 ActionClass 的略微修改版本,该版本删除了泛型,因为它们与您遇到的特定问题无关:

interface ActionClass {
  [actionKey: string]: (action: Actions['type']) => void
}

TypeScript 的一个当前限制是,当 class 实现 interface 时,编译器不使用类成员 contextually type 的接口定义。在class Foo implements Bar {...} 中,编译器将在完全不咨询Bar 的情况下推断Foo 的类型;只有在此之后,它才会根据Bar 检查该类型。请参阅microsoft/TypeScript#32082 和其中的许多链接问题以了解更多信息。

现在,如果您希望 class Foo implements Bar {...} 工作,您需要手动明确地为 Foo 的成员提供编译器所需的一切,以便推断的 Foo 类型可以分配给 Bar

现在CardActions 被推断为好像你写了这个:

class CardActions {
  constructor(private dispatch: Dispatch<Actions>) { }
  disp(action) { }
}

因为你没有给CardActionsindex 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']) =&gt; void 类型的函数。但是在您的构造函数中,您已将private 关键字放在dispatch 参数上,使其成为parameter property。这是类似以下内容的简写:

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&lt;Actions&gt;,但索引签名要求每个属性都可以分配给 (action: Actions['type']) =&gt; void。并且 Dispatch&lt;Actions&gt; 不能分配给它(参数类型错误)。

我不确定解决此问题的正确方法是什么,因为我不确定您是否真的想要编写的索引签名或者您真的想要 dispatch 属性。但是你不能两者都没有错误。让我们删除 dispatch 属性只是为了让错误消失:

class CardActions implements ActionClass {
  constructor(dispatch: Dispatch<Actions>) { }
  [actionKey: string]: (action: Actions['type']) => void;
  disp(action: Actions['type']) { }
}

万岁,没有错误!可能有一百种不同的可能方法可以用来代替这个,但探索这些可能超出了这个问题的范围。


所以你去。如果希望类实现具有字符串索引签名的接口,则需要在类定义中显式声明索引签名,并且需要确保类的每个已知属性都与索引签名兼容。

Playground link to code

【讨论】:

  • 为什么不用Dispatch&lt;Actions&gt; 的联合类型?我相信这是options the manual recommends 之一。编辑:不是手册……我明白了……
  • 当然,这是一种方法。有很多方法可以继续,但如果没有所需的用例,这都是推测性的。
猜你喜欢
  • 1970-01-01
  • 2023-03-26
  • 2013-09-12
  • 2018-10-29
  • 1970-01-01
  • 2016-11-28
  • 2019-12-10
  • 2014-10-09
相关资源
最近更新 更多