Turfjs / turf

A modular geospatial engine written in JavaScript and TypeScript

Home Page:https://turfjs.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Infer `id` existence in type definitions for `feature` function

AminoffZ opened this issue · comments

In the context of using the feature function from @turf/helpers, I've noticed that the TypeScript type definitions could benefit from improved inference for the id field. Specifically, in scenarios where an id is provided, it would be advantageous for the type system to recognize its existence and reflect this in the returned type.

Reproduction:
Consider the following example:

import { Feature, Geometry } from "geojson";
import { Id, feature } from "@turf/helpers";

let coords: Geometry = {
  type: "Point",
  coordinates: [0, 0],
};

type FeatureWithId = Feature & Required<Pick<Feature, "id">>;;

let houseFeature: FeatureWithId = feature(coords, null, { id: "house" });
houseFeature.id;

Given the @turf/helpers and geojson types, and the explicit provision of an id, one might expect this code to be valid. However, the current type definitions do not infer the presence of the id and a TypeScript error results.

Expected Behavior:
When an id is provided to the feature function, the type system should recognize its existence in the returned feature, preventing TypeScript errors in similar scenarios.

Actual Behavior:
TypeScript raises an error because it doesn't infer the existence of the id field even when it's provided.

Suggestion:
Enhance the type definitions in @turf/helpers to infer the existence of the id field when it's explicitly provided to the feature function.

export declare function feature<G extends GeometryObject, P = GeoJsonProperties>(geom: G | null, properties: P | undefined, options: {
    bbox: BBox;
    id: Id;
}): Feature<G, P> & Required<Pick<Feature<G, P>, "bbox" | "id">>;
export declare function feature<G extends GeometryObject, P = GeoJsonProperties>(geom: G | null, properties: P | undefined, options: {
    bbox: BBox;
    id?: never;
}): Feature<G, P> & Required<Pick<Feature<G, P>, "bbox">>;
export declare function feature<G extends GeometryObject, P = GeoJsonProperties>(geom: G | null, properties: P | undefined, options: {
    bbox?: never;
    id: Id;
}): Feature<G, P> & Required<Pick<Feature<G, P>, "id">>;
export declare function feature<G extends GeometryObject, P = GeoJsonProperties>(geom: G | null, properties?: P, options?: {
    bbox?: BBox;
    id?: Id;
}): Feature<G, P>;

EDIT:
For certain types of state management, this change would be really handy. Instead of wrapping entities with a unique getter id and then mapping features to set it, we could just use type Entity = Feature & Required<Pick<Feature, "id">>; and directly work with the id from the get-go.