提问人:Martin Hansson 提问时间:6/4/2023 最后编辑:Martin Hansson 更新时间:9/21/2023 访问量:54
内部类型的泛型 Typeguard
Generic Typeguard for Inner type
问:
所以我遇到了一些问题,在工作中我们使用这些类型:
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;
}
我们遇到的问题是,当我们想要缩小范围时,我们最终不得不在调用站点为每个功能创建一个 Typeguard。Feature<Geometry>
export function isPolygon(f: Feature): f is Feature<Polygon> {
return f && f.geometry && f.geometry.type === 'Polygon';
}
我想做一个通用的typeguard,我使用Generic Typeguard作为我思考的基础,但我被卡住了。
我知道我可以使用 来获取我的类型,例如这个例子。Extract<Geometry, {type: U}>
export function isFeature<T extends Feature<Geometry>, U extends T['geometry']['type']>(
feature: T,
type: U
): feature is Feature<Extract<Geometry, { type: U }>> {
return feature && feature.geometry && feature.geometry.type === type;
}
我遇到的问题是,如果我将 T 限制为 Feature 类型,则表达式中的 T 变量没有用处,它会给我一个错误,说,如果我将其限制为 Geometry,那么我不能使用 Cause,有没有办法为功能提供通用类型保护?A type Predicate's type must be assignable to it's parameter type
Feature<Extract<T, {type: U}>>
T could be instantiated with an arbitrary type which could be unrelated
答:
问题在于,在类型 guard 中,您键入 as 并检查是否是 以外的其他内容,这在逻辑上是不可能转换为 的。相反,您应该只接受并保留非泛型,只是 .feature
T
feature
T
T
Feature<Extract<Geometry, { type: U }>>
U
feature
unknown
isFeature<U extends Feature<Geometry>["geometry"]["type"]>(
feature: unknown,
type: U
): feature is Feature<Extract<Geometry, { type: U }>> {}
这将导致许多重复检查,为了避免它们,我们可以创建一个泛型类型保护,它将任何对象类型转换为 .将属性转换为并使整个对象成为部分对象的原因是为了使其尽可能类型安全:isObject
T
Partial<Record<keyof T, unknown>>
unknown
const isObject = <T>(
arg: unknown
): arg is Partial<Record<keyof T, unknown>> => {
return !!arg && typeof arg === "object" && !Array.isArray(arg);
};
接下来,我们可以为通用特征提取类型保护,理想情况下,您还将包括 into 检查:geometry.coordinates
const isFeatureBase = (arg: unknown): arg is Feature => {
return (
isObject<Feature>(arg) &&
isObject<Feature["geometry"]>(arg.geometry) &&
typeof arg.type === "string" &&
typeof arg.geometry.type === "string"
);
};
最后,我们将如下所示:isFeature
function isFeature<U extends Feature<Geometry>["geometry"]["type"]>(
feature: unknown,
type: U
): feature is Feature<Extract<Geometry, { type: U }>> {
return isFeatureBase(feature) && feature.geometry.type === type;
}
用法:
const a = {};
if (isFeature(a, "MultiPoint")) {
// "MultiPoint"
a.geometry.type;
}
评论
!!arg
typeof null === 'object'
object
arg
IsObject
export function isFeature<U extends Feature<Geometry>["geometry"]["type"]>( feature: Feature, type: U ): feature is Feature<Extract<Geometry, { type: U }>> { return feature && feature.geometry && feature.geometry.type === type; }
feature
isFeature(feature as Feature)
我自己也遇到了这个问题,并想分享一下,如果您已经验证了对象是特征,您只想缩小几何类型,该如何做到这一点:
function isFeature<T extends Feature["geometry"]["type"]>(
feature: Feature,
type: T,
): feature is Feature<Extract<Geometry, { type: T }>> {
return feature.geometry.type === type;
}
if (isFeature(foo, "Polygon")) {/* narrows foo to Feature<Polygon> */}
这适用于 @types/geojson。
评论