如何实现Geometry类型的通用Type Guard解决类型收窄问题?
通用Geometry类型守卫的正确实现方案
一、基础类型定义
我们工作中使用以下TypeScript类型作为Geometry的基础:
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类型单独编写Type Guard,例如判断Point的守卫:
export const isPointGeometry = (g: Geometry): g is Point => { return (g as Point).type === 'Point'; };
这种方案虽可行,但新增Geometry类型时需重复编写类似代码,效率较低。
三、无效的通用类型守卫尝试
尝试编写通用Type Guard,但实现无效,无法正确收窄类型:
export function isGeometry<T extends Geometry, G extends GeoJsonObject['type']>(feature: T, geometry: G): feature is T { return feature && feature.type === geometry; }
在如下函数中使用时,f.geometry仍为Point | MultiPoint联合类型,无法正确获取Point类型的coordinates:
export function coordinatesFor(f: Feature<Point | MultiPoint>) { if (isGeometry(f.geometry, 'Point')) { return f.geometry.coordinates ?? []; // 此处f.geometry未被收窄为Point } return []; }
四、正确的通用类型守卫实现
核心是建立GeoJSON类型字符串与具体Geometry类型的映射,让TypeScript能根据传入的类型字符串推断出对应的具体类型:
步骤1:定义类型映射表
先创建一个映射类型,把每个GeoJSON类型字符串关联到对应的Geometry接口:
// 建立类型字符串到具体Geometry类型的映射 type GeometryTypeMap = { 'Point': Point; 'MultiPoint': MultiPoint; };
步骤2:实现通用类型守卫
利用这个映射表编写通用守卫,让泛型参数关联到映射表的键,从而实现精准的类型推断:
export function isGeometry<T extends keyof GeometryTypeMap>( geometry: Geometry, type: T ): geometry is GeometryTypeMap[T] { return geometry.type === type; }
步骤3:验证使用效果
现在在函数中使用这个通用守卫,TypeScript会正确收窄类型:
export function coordinatesFor(f: Feature<Point | MultiPoint>) { if (isGeometry(f.geometry, 'Point')) { // f.geometry已被收窄为Point,coordinates为number[]类型 return f.geometry.coordinates ?? []; } // 此处f.geometry自动收窄为MultiPoint return f.geometry.coordinates.flat() ?? []; }
优势说明
- 无需重复编写单个类型守卫,新增Geometry类型时,只需在
GeometryTypeMap中添加对应键值对即可 - 类型推断精准,完全符合TypeScript的类型收窄规则
内容的提问来源于stack exchange,提问作者Martin Hansson
相关产品推荐
相关产品推荐

