Select to view content in your preferred language

How to use jsdoc with @arcgis/core for typing help

609
4
Jump to solution
10-28-2024 04:10 PM
RyanSutcliffe
Frequent Contributor

I am starting to use the @arcgis/core library instead of the old esriLoader pattern for some ESRI ArcGIS Maps SDK apps I have. Its nice to have some typing support in my IDE this way. However, I am often getting errors that properties and methods do not exist on my types even though I know that they do.

For example, in typescript apps I can do:

import Point from "@arcgis/core/geometry/Point";
import Polyline from "@arcgis/core/geometry/Polyline";
import FeatureLayer from "@arcgis/core/layers/FeatureLayer";
import GraphicsLayer from "@arcgis/core/layers/GraphicsLayer";
import Layer from "@arcgis/core/layers/Layer";

export function graphicsLayerFn(ptGraphicsLayer: GraphicsLayer) {
  // huh a GraphicsLayer does not have graphics property?
  ptGraphicsLayer.graphics.items.array.forEach((element: any) => {
    return element.id; // do something
  });
}

export function reprojectPoint(startPt: Point, endPt: Point) {
  const myPolyline = new Polyline();
  myPolyline.addPath([startPt, endPt]);
}

export function getLayerId(layer: Layer | FeatureLayer) {
  return layer.id;
}

export async function fetchExtent(layer: FeatureLayer) {
  //https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#queryExtent
  const response = await layer.queryExtent();
  return response.extent.toJSON();
}

export function addEventToLayer(layer: Layer) {
  layer.on("layerview-create", () => {
    console.log("Success");
  });

  layer.on("layerview-create-error", (e) => {
    console.warn("error");
  });
}

 

And all typings are good there.

But if I try to do this in javascript using JsDoc approach there are all sorts of issues:

import Polyline from '@arcgis/core/geometry/Polyline';

/**
 *
 * @param {import("@arcgis/core/layers/GraphicsLayer")} ptGraphicsLayer
 */
function graphicsLayerFn(ptGraphicsLayer) {
  // huh a GraphicsLayer does not have graphics property?
  ptGraphicsLayer.graphics.items.array.forEach((element) => {
    return true; // do something
  });
}

/**
 *
 * @param {import("@arcgis/core/geometry/Point")} startPt
 * @param {import("@arcgis/core/geometry/Point")} endPt
 */
function reprojectPoint(startPt, endPt) {
  const myPolyline = new Polyline();

  // huh, the docs say that add path can take an array of Point or number but the
  // typing check says a geometry is needed???
  // https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-Polyline.html#addPath
  myPolyline.addPath([startPt, endPt]);
}

/**
 *
 * @param {import("@arcgis/core/layers/Layer")} layer
 * @returns
 */
function getLayerId(layer) {
  // the base layer type has a property of id? Not sure why this is
  // an error:
  return layer.id;
}

// how do we use the @ArcGIS/core types when we are not actually
// importing the library in the file itself? /// triple-slash directive?
// Or why are they sometimes not found

/**
 *  @param {import("@arcgis/core/layers/FeatureLayer")} layer footprint/stack layer
 *  @returns {Promise<{spatialReference: object, xmin: number, ymin: number, ymax: number, xmax: number} | {error: string}>} extent or error?
 */
async function fetchExtent(layer) {
  //https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#queryExtent
  let response = await layer.queryExtent();
  return response.extent.toJSON();
}

/**
 * @param {import("@arcgis/core/layers/FeatureLayer")} layer
 */
function addEventToLayer(layer) {
  layer.on("layerview-create", () => {
    console.log("Success");
  });

  layer.on("layerview-create-error", (e) => {
    console.warn("error");
  });
}

 

IDEErrors.gif

I suspect I'm doing something wrong here with how I import these types. Does anyone know what the trick or proper pattern is-- specifically for JavaScript and JSDoc based type hints?

 

Here is a gitlab repo with the code so you can see what I mean in an IDE like Visual Studio Code.

0 Kudos
1 Solution

Accepted Solutions
Sage_Wall
Esri Regular Contributor

Hi @RyanSutcliffe ,

You need to import the classes at the top of your file.  Typescript allows you to import just the type but sadly JavaScript does not.  Then just use that named class as your `@param` in the JSDoc.  You may see some linting errors about the imports not being used though.  

import Polyline from '@arcgis/core/geometry/Polyline';
import Map from '@arcgis/core/Map';
import MapView from '@arcgis/core/views/MapView';
import Point from '@arcgis/core/geometry/Point';
import GraphicsLayer from '@arcgis/core/layers/GraphicsLayer';
import Layer from '@arcgis/core/layers/Layer';
import FeatureLayer from '@arcgis/core/layers/FeatureLayer';

const map = new Map({
  basemap: "topo-vector",
});

new MapView({
  container: "viewDiv",
  map: map,
  zoom: 4,
  center: [15, 65], // longitude, latitude
});

/**
 *
 * @param {GraphicsLayer} ptGraphicsLayer
 */
function graphicsLayerFn(ptGraphicsLayer) {
  // huh a GraphicsLayer does not have graphics property?
  ptGraphicsLayer.graphics.forEach(() => {
    return true; // do something
  });
}

/**
 *
 * @param {Point} startPt
 * @param {Point} endPt
 */
function reprojectPoint(startPt, endPt) {
  const myPolyline = new Polyline();

  // huh, the docs say that add path can take an array of Point or number but the
  // typing check says a geometry is needed???
  // https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-Polyline.html#addPath
  myPolyline.addPath([startPt, endPt]);
}

/**
 *
 * @param {Layer} layer
 * @returns
 */
function getLayerId(layer) {
  // the base layer type has a property of id? Not sure why this is
  // an error:
  return layer.id;
}

// how do we use the @arcgis/core types when we are not actually
// importing the library in the file itself? /// triple-slash directive?
// Or why are they sometimes not found

/**
 *  @param {FeatureLayer} layer footprint/stack layer
 *  @returns {Promise<{spatialReference: object, xmin: number, ymin: number, ymax: number, xmax: number} | {error: string}>} extent or error?
 */
async function fetchExtent(layer) {
  //https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#queryExtent
  let response = await layer.queryExtent();
  return response.extent.toJSON();
}

/**
 * @param {FeatureLayer} layer
 */
function addEventToLayer(layer) {
  layer.on("layerview-create", () => {
    console.log("Success");
  });

  layer.on("layerview-create-error", (e) => {
    console.warn("error");
  });
}

 

View solution in original post

4 Replies
ReneRubalcava
Esri Frequent Contributor

When you import the class and you type it like that, ptGraphicsLayer is of type constructor for Graphic, so it's like your telling TS it can do "new ptGraphicsLayer()".

You can use a utility type to help you out here with InstanceType.

import Polyline from "@arcgis/core/geometry/Polyline";
import type FeatureLayer from "@arcgis/core/layers/FeatureLayer";
import type GraphicsLayer from "@arcgis/core/layers/GraphicsLayer";
import type Layer from "@arcgis/core/layers/Layer";
import { Point } from "@esri/calcite-components/dist/types/components/graph/interfaces";

export function graphicsLayerFn(ptGraphicsLayer: InstanceType<typeof GraphicsLayer>) {
  ptGraphicsLayer.graphics.forEach((element: any) => {
    return element.id; // do something
  });
}

 

Now everything is much stricter and you need to update the code a bit. There is no public graphicsLayer.items.array property. You might see it in devtools, but it's not publicly typed or doc'd like that, so I updated the code to reflect those changes.

Also anything you import that is only for types, you can add the "import type", it's really more for organization. TS should strip the import if it's unused or other bundlers will. But it will also give you a warning if you try to use it as anything other than a type, which can be useful.

RyanSutcliffe
Frequent Contributor

Thanks! @ReneRubalcava , that is really helpful. I appreciate the corrections re: my typescript statement and tips on how to make use of that properly. Also bonus points for noting that the private property usage and pointing out a fix for that!

However I'm still stuck with how to make use of JSDoc types for my JavaScript code. I was previously using the @types/arcgis-js-api library on NPM. That worked because it was specifically the types in a type declariation file but its deprecated and I think, only goes to 4.28.

I can see the @arcgis./core library has type declaration files within it which should work but I cannot figure out how to reference them specifically.

I know this is more of a JSDocs question but hoping someone might have advice here.

0 Kudos
Sage_Wall
Esri Regular Contributor

Hi @RyanSutcliffe ,

You need to import the classes at the top of your file.  Typescript allows you to import just the type but sadly JavaScript does not.  Then just use that named class as your `@param` in the JSDoc.  You may see some linting errors about the imports not being used though.  

import Polyline from '@arcgis/core/geometry/Polyline';
import Map from '@arcgis/core/Map';
import MapView from '@arcgis/core/views/MapView';
import Point from '@arcgis/core/geometry/Point';
import GraphicsLayer from '@arcgis/core/layers/GraphicsLayer';
import Layer from '@arcgis/core/layers/Layer';
import FeatureLayer from '@arcgis/core/layers/FeatureLayer';

const map = new Map({
  basemap: "topo-vector",
});

new MapView({
  container: "viewDiv",
  map: map,
  zoom: 4,
  center: [15, 65], // longitude, latitude
});

/**
 *
 * @param {GraphicsLayer} ptGraphicsLayer
 */
function graphicsLayerFn(ptGraphicsLayer) {
  // huh a GraphicsLayer does not have graphics property?
  ptGraphicsLayer.graphics.forEach(() => {
    return true; // do something
  });
}

/**
 *
 * @param {Point} startPt
 * @param {Point} endPt
 */
function reprojectPoint(startPt, endPt) {
  const myPolyline = new Polyline();

  // huh, the docs say that add path can take an array of Point or number but the
  // typing check says a geometry is needed???
  // https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-Polyline.html#addPath
  myPolyline.addPath([startPt, endPt]);
}

/**
 *
 * @param {Layer} layer
 * @returns
 */
function getLayerId(layer) {
  // the base layer type has a property of id? Not sure why this is
  // an error:
  return layer.id;
}

// how do we use the @arcgis/core types when we are not actually
// importing the library in the file itself? /// triple-slash directive?
// Or why are they sometimes not found

/**
 *  @param {FeatureLayer} layer footprint/stack layer
 *  @returns {Promise<{spatialReference: object, xmin: number, ymin: number, ymax: number, xmax: number} | {error: string}>} extent or error?
 */
async function fetchExtent(layer) {
  //https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#queryExtent
  let response = await layer.queryExtent();
  return response.extent.toJSON();
}

/**
 * @param {FeatureLayer} layer
 */
function addEventToLayer(layer) {
  layer.on("layerview-create", () => {
    console.log("Success");
  });

  layer.on("layerview-create-error", (e) => {
    console.warn("error");
  });
}

 

RyanSutcliffe
Frequent Contributor

Thanks @Sage_Wall that works I think. At first glance I had a bit of trepidation about importing all these types just for JSDoc types. Would that get tree shaken when the code is built or could this pattern add unnecessary size? But I guess, all these classes and imported functions are used somewhere in the codebase so maybe that's not an issue.

Accepting as the answer.

0 Kudos