【问题标题】:How can I specify a class index signature that excludes known inherited index keys?如何指定排除已知继承索引键的类索引签名?
【发布时间】:2019-09-12 17:06:14
【问题描述】:

我想创建一个继承类的类,并为父类中不存在的所有新类属性指定索引签名。

考虑一种情况,您希望为搜索查询指定某种格式,该格式可以在字段上采用搜索词数组,并且继承自通用查询类。

class Query {
  limit = 25;
  offset?: number;
  constructor() { /* do stuff */ }
}

class FieldSearchQuery extends Query {
  [key: Exclude<string, keyof Query>]: string[]; // not valid, but captures the gist
  constructor() { super(); /* do stuff */ }
}

对替代品有什么想法吗?请注意,有一个现有的基类,我的目标是缩小子类的类型,以便它可以使用不与父键相交的任何 new 键值 并且 我们应该能够对与那些未知键关联的值进行类型检查,确保它们是已定义的类型(在上面的示例中,字符串数组)。

我一直在查看 Advanced Types 文档,感觉好像我一定遗漏了什么,因为似乎存在完成此任务的所有工具。

编辑: 我只是想出了一个可能的改进,您至少可以排除父级中不存在的所有值类型:

class Query {
  limit = 25;
  offset?: number;
  constructor() { /* do stuff */ }
}

class FieldSearchQuery extends Query {
  [key: string]: Query[keyof Query]|string[]; // valid, but overly broad
  constructor() { super(); /* do stuff */ }
}

【问题讨论】:

  • 是否有超过 2 个类或用例是什么?这不会总是排除所有 Query 键,因为它正在扩展它吗?
  • 这确实会排除索引签名中的所有查询键,但在类本身上需要它们。这种情况涉及从父类继承的多个类。
  • 为了进一步澄清,您希望能够通过使用该类的任何内容来提供 FieldSearchQuery 所需的任何字段名称作为键,并检查那些 new 值(即不在查询中的值)始终是索引签名指定的值。使用签名 { name: "Joe" }(获得默认值 { name: "Joe", limit: 25, constructor: fn() ...etc })的搜索应该键入错误,因为 name 键未解析为字符串数组)
  • 再澄清一点:问题是子类的索引签名与父类的签名相交。 limit = 25 不是子类上索引签名 [key: string]: string[] 的有效键/值。

标签: typescript typescript-generics


【解决方案1】:

您确定这里的模式正确吗?因为如果你在一个类上指定一个索引签名,它不仅在类属性上,而且在所有方法上。

class B {
  [key: string]: string[]

  foo(): number { // Property 'foo' of type '() => number' is not assignable to string index type 'string[]'.
    return 1
  }
}

实现您的解决方案的一种方法是遵循打字稿文档中的intersection types example

我的解决方案是在你的类上声明一个字段,并指定它的类型。

type QueryParams = { [key: string]: any } & {
  limit: number
  offset?: number
}

class Query<T extends QueryParams> {
  params: T
  constructor(params: Partial<T>) {
    this.params.limit = params.limit === undefined ? 25 : params.limit
  }
}

type FieldSearchQueryParams = { [key: string]: string[] } & {
  limit: number
  offset?: number
}

class FieldSearchQuery extends Query<FieldSearchQueryParams> {
  constructor(params: Partial<FieldSearchQueryParams>) {
    super(params)
    // do stuff
  }
}

【讨论】:

  • 要点和解决方案也很棒!谢谢你。我有一种感觉,交集类型将成为方式,但我仍然没有考虑索引签名和类方法之间的关系。我想我太执着于相信我想要的缩小范围可以用一种不那么冗长的方式来实现。
猜你喜欢
  • 2018-12-20
  • 1970-01-01
  • 2020-12-05
  • 2020-06-27
  • 2022-10-07
  • 1970-01-01
  • 2013-01-27
  • 2018-12-30
  • 2023-03-15
相关资源
最近更新 更多