Типизация с TypeScript

Turf.js изначально проектировался как библиотека для работы с геоданными в формате GeoJSON, что естественным образом делает его хорошо совместимым с TypeScript. Типизация в данном контексте решает две ключевые задачи: строгую проверку геометрических структур и безопасную работу с функциями пространственного анализа.

Основой типовой системы Turf.js выступают стандарты GeoJSON, формализованные через набор интерфейсов: Feature, FeatureCollection, Geometry, а также конкретные геометрические типы (Point, Polygon, LineString и другие).


Базовые GeoJSON-типы в TypeScript

TypeScript-экосистема Turf.js опирается на типы GeoJSON, определяющие структуру геометрических объектов.

Feature

import { Feature } fr om "geojson";

const point: Feature = {
  type: "Feature",
  geometry: {
    type: "Point",
    coordinates: [30.5, 50.5],
  },
  properties: {},
};

Feature представляет собой контейнер, который объединяет геометрию и произвольные свойства. В строгой типизации важно учитывать, что geometry может быть null.


FeatureCollection

import { FeatureCollection } from "geojson";

const collection: FeatureCollection = {
  type: "FeatureCollection",
  features: [
    {
      type: "Feature",
      geometry: {
        type: "Point",
        coordinates: [10, 20],
      },
      properties: {},
    },
  ],
};

FeatureCollection используется для группировки множества геообъектов и является основным форматом входных и выходных данных большинства функций Turf.js.


Geometry типы

import { Point, Polygon, LineString } from "geojson";

const p: Point = {
  type: "Point",
  coordinates: [0, 0],
};

Каждый геометрический тип строго фиксирует структуру координат:

  • Point[number, number]
  • LineStringnumber[][]
  • Polygonnumber[][][]

Типизация координат и проблема точности

В Turf.js координаты всегда представлены в формате [longitude, latitude], что принципиально важно при строгой типизации.

type Position = [number, number];

const coord: Position = [37.6173, 55.7558];

Ошибки порядка координат не выявляются TypeScript на уровне типов, что делает необходимым введение дополнительных утилитарных типов или runtime-проверок.


Типы функций Turf.js

Функции Turf.js в TypeScript обычно принимают строго типизированные GeoJSON-объекты и возвращают новые Feature.

Пример: turf.point

import point from "@turf/point";

const p = point([10, 20]);

Тип результата:

Feature<Point>

Пример: turf.buffer

import buffer from "@turf/buffer";
import { point } from "@turf/helpers";

const pt = point([30, 10]);

const result = buffer(pt, 50, { units: "kilometers" });

Тип результата:

Feature<Polygon>

TypeScript выводит тип на основании входной геометрии, однако в некоторых случаях требуется явное указание дженериков.


Дженерики в Turf.js

Некоторые функции используют дженерики для сохранения типа входного Feature.

import { Feature, Point } from "geojson";
import centroid from "@turf/centroid";

const input: Feature<Point> = {
  type: "Feature",
  geometry: {
    type: "Point",
    coordinates: [0, 0],
  },
  properties: {},
};

const result = centroid(input);

Тип результата:

Feature<Point>

Однако для сложных операций результат может быть изменён, например Point → Polygon.


Типизация свойств (Properties)

properties в GeoJSON по умолчанию имеют тип:

Record<string, any>

Это создаёт слабое место в типизации Turf.js, которое часто уточняется вручную.

Строгая типизация properties

interface MyProps {
  name: string;
  population: number;
}

import { Feature } from "geojson";

const city: Feature<Point, MyProps> = {
  type: "Feature",
  geometry: {
    type: "Point",
    coordinates: [10, 20],
  },
  properties: {
    name: "City",
    population: 100000,
  },
};

Такой подход обеспечивает контроль структуры данных на уровне компиляции.


Типы входов и перегрузки функций

Turf.js активно использует перегрузки функций для поддержки разных типов входных данных.

Пример: turf.distance

import distance from "@turf/distance";

const a = [0, 0];
const b = [10, 10];

const d = distance(a, b, { units: "kilometers" });

Функция принимает:

  • Position
  • Feature<Point>
  • Geometry<Point>

TypeScript объединяет эти варианты через union-типы:

type AllPoints = Position | Feature<Point> | Point;

Проблемы строгой типизации в Turf.js

Несмотря на поддержку TypeScript, библиотека имеет ряд типовых ограничений:

1. Слабая проверка координат

const wrong = point(["invalid" as any, 20]);

TypeScript не предотвращает runtime-ошибку без дополнительных ограничений.


2. Универсальные any в утилитах

Некоторые вспомогательные функции используют обобщённые any для properties.


3. Потеря точного типа Feature

После трансформаций тип часто становится:

Feature<Geometry, any>

что снижает пользу строгой типизации.


Расширение типов Turf.js

Создание собственных строго типизированных функций

import { Feature, Point } from "geojson";
import { point } from "@turf/helpers";

interface UserProps {
  id: number;
}

type UserPoint = Feature<Point, UserProps>;

function createUserPoint(
  coords: [number, number],
  props: UserProps
): UserPoint {
  return {
    ...point(coords),
    properties: props,
  };
}

Интеграция с strict mode TypeScript

Включение строгого режима усиливает контроль:

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}

В этом режиме:

  • обязательна проверка null в geometry
  • запрещены неявные any
  • усиливается контроль union-типов

Типизация результатов геоопераций

Union-результаты

Некоторые операции возвращают разные типы в зависимости от входа:

Feature<Point> | Feature<Polygon>

Это характерно для:

  • intersect
  • union
  • difference

Пример union

import union from "@turf/union";

const result = union(poly1, poly2);

Тип результата:

Feature<Polygon | MultiPolygon> | null

Работа с массивами GeoJSON

FeatureCollection и map-операции

import { FeatureCollection } from "geojson";
import centroid from "@turf/centroid";

function process(fc: FeatureCollection) {
  return fc.features.map((f) => centroid(f));
}

Тип результата:

Feature<Point>[]

Пользовательские типы для геоаналитики

При построении сложных систем анализа часто вводятся доменные типы поверх GeoJSON.

type Road = Feature<LineString, { speedLim it: number }>;
type Building = Feature<Polygon, { floors: number }>;

Это позволяет связывать геометрию с бизнес-логикой без потери типовой строгости.


Типизация модулей и tree-shaking

Turf.js поддерживает модульный импорт:

import area from "@turf/area";
import length from "@turf/length";

TypeScript корректно типизирует каждый модуль отдельно, что снижает:

  • размер бандла
  • вероятность конфликтов типов
  • зависимость от глобальных namespace

Типы в геометрических преобразованиях

Преобразование Point → Buffer (Polygon)

import buffer from "@turf/buffer";
import { point } from "@turf/helpers";

const p = point([0, 0]);

const poly = buffer(p, 10, { units: "meters" });

Тип результата:

Feature<Polygon>

TypeScript фиксирует изменение геометрии, но не отслеживает семантическую корректность.


Обобщённая модель типизации Turf.js

Типовая система Turf.js может быть сведена к нескольким уровням:

  • базовые GeoJSON-типы (Feature, Geometry)
  • геометрические специализации (Point, Polygon)
  • дженерики для properties
  • перегрузки функций для входных данных
  • union-типы для результатов геоопераций

Эта модель обеспечивает баланс между гибкостью геоалгоритмов и статической безопасностью TypeScript, сохраняя совместимость с динамическими сценариями пространственного анализа.