通用 Typeguard

Generic Typeguard

提问人:Martin Hansson 提问时间:6/3/2023 最后编辑:Martin Hansson 更新时间:6/4/2023 访问量:62

问:

因此,在工作中,我们使用这些类型来创建几何图形的基础:

export type GeoJSON = Geometry | Feature;

export type GeoJsonTypes = GeoJSON['type'];

export interface GeoJsonObject { type: GeoJsonTypes; }

export interface Point extends GeoJsonObject {
   type: 'Point';
   coordinates: number[];
 }

export interface MultiPoint extends GeoJsonObject {
   type: "MultiPoint",
   coordinates: number[][];
 }

export type Geometry =
 | Point
 | MultiPoint

export interface Feature<G extends Geometry = Geometry>
  extends GeoJsonObject {
  type: 'Feature';
  geometry: G;
}

问题在于,我们最终必须为每个 Geometry 创建单独的 typeguard:

export const isPointGeometry = (g: Geometry): g is Point => {
  return (g as Point).type === 'Point';
};

我试图通过编写以下内容来创建一个通用的typeguard:

export function isGeometry<T extends Geometry, G extends GeoJsonObject['type']>(feature: T, geometry: G): feature is T {
  return feature && feature && feature.type === geometry;
}

但无济于事。因为我仍然有问题?有人有想法吗?

export function coordinatesFor(f: Feature<Point | MultiPoint>) {
  if (isGeometry(f.geometry, 'Point')) {
   return f.geometry.coordinates ?? []; // Expected Feature<Point> but got Feature<Point | MultiPoint>
  }
  return [];
 }
TypeScript Typeguards 类型缩小

评论

0赞 Thomas Renger 6/3/2023
只要类型 auf 特征(在 you guard 中是 T)和返回类型(在 you guard 中是 T),您只需证明类型就是它。因此,您需要将这些类型分开,并且需要使用此类型调用函数:isGeometry<Polygon>(...)但从我的角度来看,这是没有意义的。由于您无法检查您提供给函数的泛型类型,因此它只是安全性较低的好代码。最好是每种类型都有一个防护装置。
0赞 jcalz 6/3/2023
请考虑编辑代码,以便它成为一个最小的可重现示例,如果有人将其复制并粘贴到独立 IDE 中而没有不相关的问题(例如,未声明的类型等),则演示您正在谈论的内容。这将使人们立即开始工作,并增加您获得良好答案的机会。除非问题依赖于 geojson 来理解,否则代码最好不要引用它;您能否将问题标记为依赖于 geojson,在示例中导入相关类型,或者更好的是:将它们替换为示例类型?feature && feature
0赞 Martin Hansson 6/3/2023
谢谢@jcalz我已经更新了一个 Playground 的示例,它有点难以缩小范围,因为它有点依赖于类型,但希望它足够小
0赞 jcalz 6/3/2023
我看到你已经链接到一个游乐场,这很棒。但是您也应该将其作为明文复制到问题中,使其成为一个最小的可重现示例。这些是 SO 规则(任何人都不应该被要求离开 SO 来查看示例代码)。我会为你做这件事,但我需要明确的许可,因为它比任何人都不清楚,但你有权复制外部托管的代码(是的,我知道代码在 URL 中是 base64 编码的,但从法律上讲,不清楚什么算作复制)
0赞 jcalz 6/3/2023
但是:现在看这个例子,我的解决方案看起来就像下面已经发布的一样,所以这就是要走的路。(不过,请继续编辑它)

答:

2赞 darbaidze2020 6/3/2023 #1

试试这个方法:

function isGeometry<T extends Geometry, G extends T['type'] = Geometry['type']>(
  feature: T,
  geometry: G,
): feature is Extract<T, { type: G }> {
  return feature && feature && feature.type === geometry;
}

在这里,我们做两件事:

  • 我们根据参数推断类型,以确保始终与给定兼容,并且不可能使用错误的参数组合调用函数,例如:。GTGTisGeometry(somePoint, 'Polygon')

  • 对于返回类型,我们选择那些与(我们正在寻找的几何图形)兼容的联合成员。Extract<T, { type: G }>Geometry{ type: G }

评论

0赞 Martin Hansson 6/4/2023
谢谢你,它工作得很好!你介意解释一下它是如何工作的吗,我不明白我为什么需要?我想我理解为什么我们需要它基本上是因为我们想根据类型缩小几何形状,例如“点”,这是一个唯一的标识符,因此总是给我们留下一个选项,这就是让我们实际上缩小范围并为我们提供所需的类型保护的原因。= Geometry['type']Extract<T, {type: G}>
0赞 darbaidze2020 6/4/2023
@MartinHansson,如果我们(出于某种原因)想显式传递第一个参数,我使用等号使第二个参数(泛型)对于这种情况是可选的。在我们的例子中,无论我们是否使用或为此,两者都以相同的方式工作。P.S:我在答案中添加了解释。= Geometry['type']= Geometry['type']= T['type']