Skip to content

TypeScript Support: JSDoc annotations or full migration? #169

Description

@mazipan-wego

Feature Request: TypeScript Support

Hello! First, thanks for building this library — it's been useful for our project.

We're wondering if there are any plans to add TypeScript support. We see two common paths forward and would love to know if either is on your roadmap:


Option 1: JSDoc Type Annotations (lighter lift)

If a full TypeScript migration isn't on the roadmap yet, adding JSDoc type annotations is a great middle ground. It gives consumers type safety and IntelliSense without changing the source language.

Reference: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html

Suggested starting point — the customer-facing API first:

The most impactful place to start is src/components/SeatMap/SeatMap.js — the primary component consumers interact with. Annotate in this order:

Step 1 — Define shared types at the top of SeatMap.js:

/**
 * @typedef {{ label: string, status: string, type?: string, price?: number }} AvailabilityItem
 * @typedef {{ id: string, passengerType?: string, readOnly?: boolean }} Passenger
 * @typedef {{ id: string }} Flight
 * @typedef {{ seatLabel: string | number }} SeatJumpTo
 * @typedef {{ JetsTooltip?: import('react').ComponentType<any> }} ComponentOverrides
 */

Step 2 — Annotate the JetsSeatMap component props:

/**
 * @param {object} props
 * @param {Flight} props.flight
 * @param {AvailabilityItem[]} [props.availability]
 * @param {Passenger[]} [props.passengers]
 * @param {typeof JETS_SEATMAP_DEFAULT_CONFIG} [props.config]
 * @param {number} [props.currentDeckIndex]
 * @param {SeatJumpTo} [props.seatJumpTo]
 * @param {(data: object) => void} [props.onSeatMapInited]
 * @param {(passengers: Passenger[]) => void} [props.onSeatSelected]
 * @param {(passengers: Passenger[]) => void} [props.onSeatUnselected]
 * @param {(data: object) => void} [props.onTooltipRequested]
 * @param {(data: object) => void} [props.onLayoutUpdated]
 * @param {(data: object) => void} [props.onSeatMouseLeave]
 * @param {(data: object) => void} [props.onSeatMouseClick]
 * @param {(data: object) => void} [props.onAvailabilityApplied]
 * @param {ComponentOverrides} [props.componentOverrides]
 */
export const JetsSeatMap = ({ ... }) => { ... }

Step 3 — Annotate enums in src/common/constants.js:

/** @enum {string} */
export const ENTITY_STATUS_MAP = { available: 'available', ... };

/** @enum {string} */
export const ENTITY_TYPE_MAP = { seat: 'seat', ... };

/** @enum {string} */
export const SCALE_TYPES = { ZOOM: 'zoom', SCALE: 'scale' };

Step 4 — Build tool changes:

  • Install TypeScript as a dev dependency:

    npm install --save-dev typescript
  • Create tsconfig.json at the project root:

    {
      "compilerOptions": {
        "allowJs": true,
        "checkJs": false,
        "declaration": true,
        "emitDeclarationOnly": true,
        "outDir": "dist/types",
        "moduleResolution": "node",
        "jsx": "react"
      },
      "include": ["src/**/*"],
      "exclude": ["src/**/*.test.js", "src/**/__fixtures__", "src/**/__mocks__"]
    }
  • In package.json, add the types field and a build script:

    {
      "types": "dist/types/index.d.ts",
      "scripts": {
        "build:types": "tsc -p tsconfig.json"
      }
    }
  • Run npm run build:types alongside your existing build to emit dist/types/index.d.ts automatically


Option 2: Gradual Full Migration to TypeScript (low initial friction)

A full migration doesn't have to happen all at once. By starting with a loose tsconfig, you can rename files to .ts/.tsx incrementally while the compiler accepts most existing code as-is. Tighten the config over time as more files are typed properly.

Step 1 — Install TypeScript and React types:

npm install --save-dev typescript @types/react @types/react-dom

Step 2 — Create a loose tsconfig.json to start with zero errors:

{
  "compilerOptions": {
    "target": "ES6",
    "module": "ESNext",
    "moduleResolution": "node",
    "jsx": "react",
    "allowJs": true,
    "strict": false,
    "noImplicitAny": false,
    "strictNullChecks": false,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "declaration": true,
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src/**/*"],
  "exclude": ["src/**/*.test.*", "src/**/__fixtures__", "src/**/__mocks__"]
}

The key flags here: strict: false, noImplicitAny: false, and skipLibCheck: true let the compiler accept mostly untyped code without flooding you with errors on day one.

Step 3 — Start with the entry point and primary component only:

Rename just these two files first — everything else can stay as .js since allowJs: true covers them:

  • src/index.jssrc/index.ts
  • src/components/SeatMap/SeatMap.jssrc/components/SeatMap/SeatMap.tsx

Add a basic props interface to SeatMap.tsx:

interface JetsSeatMapProps {
  flight: { id: string };
  availability?: Array<{ label: string; status: string }>;
  passengers?: Array<{ id: string; passengerType?: string }>;
  config?: Partial<typeof JETS_SEATMAP_DEFAULT_CONFIG>;
  currentDeckIndex?: number;
  seatJumpTo?: { seatLabel: string | number };
  onSeatMapInited?: (data: object) => void;
  onSeatSelected?: (passengers: object[]) => void;
  onSeatUnselected?: (passengers: object[]) => void;
  onTooltipRequested?: (data: object) => void;
  onLayoutUpdated?: (data: object) => void;
  onSeatMouseLeave?: (data: object) => void;
  onSeatMouseClick?: (data: object) => void;
  onAvailabilityApplied?: (data: object) => void;
  componentOverrides?: { JetsTooltip?: React.ComponentType<any> };
}

export const JetsSeatMap: React.FC<JetsSeatMapProps> = ({ ... }) => { ... }

Step 4 — Update the build pipeline:

If using Rollup, add the TypeScript plugin:

npm install --save-dev @rollup/plugin-typescript

Then in rollup.config.js:

import typescript from '@rollup/plugin-typescript';

export default {
  // ...existing config...
  plugins: [
    typescript({ tsconfig: './tsconfig.json' }),
    // ...other plugins
  ],
};

Step 5 — Point package.json to the generated types:

{
  "types": "dist/index.d.ts"
}

Tightening over time: Once the initial migration is stable, gradually enable stricter flags file-by-file using // @ts-nocheck to skip files not yet ready, and remove them as you type each file properly. Eventually flip strict: true in tsconfig.json when the whole codebase is covered.


Our Situation

We're integrating this library into a TypeScript project and currently working around the lack of types by writing our own local .d.ts declarations. We'd be happy to contribute if there's interest in either direction.

Would love to hear your thoughts!


Generated with Claude

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions