1

Looking at these Segment analytics docs, how do I add TypeScript type annotations to the addDestinationMiddleware function? At least how do I get TypeScript not to complain, I can type everything as any, but is there a better approach?

I don't see it as part of @types/segment-analytics, so what other hack can I use to "fix this" in my app, and get things somewhat properly typed?

I am getting this error:

Property 'addDestinationMiddleware' does not exist on type 'AnalyticsJS'.

When doing:

window.analytics.addDestinationMiddleware('Pendo', [AnalyticsPendoMiddleware]);

My full code for what I am doing is this:

type AnalyticsPendoMiddlewarePropsType = {
  payload: any;
  integration?: any;
  next: any;
};

const AnalyticsPendoMiddleware = ({
  payload,
  next,
}: AnalyticsPendoMiddlewarePropsType): void => {
  payload.visitor.id = `${payload.visitor.id}_asuffix`;
  next(payload);
};

window.analytics.addDestinationMiddleware('Pendo', [AnalyticsPendoMiddleware]);

Can I somehow override/extend @types/segment-analytics like so?

declare namespace SegmentAnalytics {
  type AddDestinationMiddelwarePropsType = {
    payload: any;
    integration?: any;
    next: any;
  }

  interface AnalyticsJS {
    addDestinationMiddleware(name: string, middleware: Array<(props: AddDestinationMiddelwarePropsType) => void>)
  }
}

If so, how would I do that exactly (that is just pseudocode)?

enter image description here

I also tried creating a ./segment-analytics.d.ts in my project, with all the stuff included:

/* eslint-disable @typescript-eslint/unified-signatures */
/* eslint-disable @typescript-eslint/no-shadow */
/* eslint-disable import/no-mutable-exports */
/* eslint-disable no-var */
/* eslint-disable vars-on-top */
/* eslint-disable @typescript-eslint/adjacent-overload-signatures */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-empty-interface */
/* eslint-disable @typescript-eslint/consistent-type-definitions */

// Type definitions for Segment's analytics.js
// Project: https://segment.com/docs/libraries/analytics.js/
// Definitions by: Andrew Fong <https://github.com/fongandrew>
//                 Miroslav Petrik <https://github.com/MiroslavPetrik>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3

declare interface JQuery {}

declare namespace SegmentAnalytics {
  // Generic options object with integrations
  interface SegmentOpts {
    integrations?: any;
    anonymousId?: string | undefined;
    context?: object | undefined;
  }

  interface CookieOptions {
    maxage?: number | undefined;
    domain?: string | undefined;
    path?: string | undefined;
    secure?: boolean | undefined;
  }

  interface MetricsOptions {
    host?: string | undefined;
    sampleRate?: number | undefined;
    flushTimer?: number | undefined;
    maxQueueSize?: number | undefined;
  }

  interface StoreOptions {
    enabled?: boolean | undefined;
  }

  interface UserOptions {
    cookie?:
      | {
          key: string;
          oldKey: string;
        }
      | undefined;
    localStorage?:
      | {
          key: string;
        }
      | undefined;
    persist?: boolean | undefined;
  }

  interface GroupOptions {
    cookie?:
      | {
          key: string;
        }
      | undefined;
    localStorage?:
      | {
          key: string;
        }
      | undefined;
    persist?: boolean | undefined;
  }

  interface InitOptions {
    cookie?: CookieOptions | undefined;
    metrics?: MetricsOptions | undefined;
    localStorage?: StoreOptions | undefined;
    user?: UserOptions | undefined;
    group?: GroupOptions | undefined;
    integrations?:
      | {
          [integration: string]: boolean | undefined;
          All?: boolean | undefined;
        }
      | undefined;
  }

  interface IntegrationsSettings {
    [key: string]: any;
  }

  interface AddDestinationMiddelwarePropsType {
    payload: any;
    integration?: any;
    next: any;
  }

  // The actual analytics.js object
  interface AnalyticsJS {
    addDestinationMiddleware(
      name: string,
      middleware: Array<(props: AddDestinationMiddelwarePropsType) => void>
    );

    /* Use a plugin */
    use(plugin: (analytics: AnalyticsJS) => void): this;

    /* Initialize with the given integration `settings` and `options`. */
    init(settings?: IntegrationsSettings, options?: InitOptions): this;

    /* Define a new integration */
    addIntegration(integration: (options: any) => void): this;

    /*  Set the user's `id`. */
    setAnonymousId(id: string): this;

    /* Configure Segment with write key */
    load(writeKey: string): void;

    /* Configure Segment with write key & integration management.

       The load method can also be modified to take a second argument,
       an object with an integrations dictionary, which used to load
       only the integrations that are marked as enabled with the boolean value true.
       works in version 4.1.0 or higher */
    load(writeKey: string, options?: SegmentOpts): void;

    /* The identify method is how you tie one of your users and their actions
       to a recognizable userId and traits. */
    identify(
      userId: string,
      traits?: Object,
      options?: SegmentOpts,
      callback?: () => void
    ): void;
    identify(userId: string, traits: Object, callback?: () => void): void;
    identify(userId: string, callback?: () => void): void;
    identify(
      traits?: Object,
      options?: SegmentOpts,
      callback?: () => void
    ): void;
    identify(traits?: Object, callback?: () => void): void;
    identify(callback: () => void): void;

    /* The track method lets you record any actions your users perform. */
    track(
      event: string,
      properties?: Object,
      options?: SegmentOpts,
      callback?: () => void
    ): void;
    track(event: string, properties?: Object, callback?: () => void): void;
    track(event: string, callback?: () => void): void;

    /* The page method lets you record page views on your website, along with
       optional extra information about the page being viewed. */
    page(
      category?: string,
      name?: string,
      properties?: Object,
      options?: SegmentOpts,
      callback?: () => void
    ): void;
    page(
      name?: string,
      properties?: Object,
      options?: SegmentOpts,
      callback?: () => void
    ): void;
    page(name?: string, properties?: Object, callback?: () => void): void;
    page(name?: string, callback?: () => void): void;
    page(
      properties?: Object,
      options?: SegmentOpts,
      callback?: () => void
    ): void;
    page(callback?: () => void): void;

    /* The group method associates an individual user with a group. The group
       can a company, organization, account, project, team or any other name
       you came up with for the same concept. */
    group(
      groupId: string,
      traits?: Object,
      options?: SegmentOpts,
      callback?: () => void
    ): void;
    group(groupId: string, traits?: Object, callback?: () => void): void;
    group(groupId: string, callback?: () => void): void;

    /* The alias method combines two previously unassociated user identities.
       This comes in handy if the same user visits from two different devices
       and you want to combine their history.

       Some providers also don’t alias automatically for you when an anonymous
       user signs up (like Mixpanel), so you need to call alias manually right
       after sign up with their brand new userId. */
    alias(
      userId: string,
      previousId?: string,
      options?: SegmentOpts,
      callback?: () => void
    ): void;
    alias(userId: string, previousId?: string, callback?: () => void): void;
    alias(userId: string, callback?: () => void): void;
    alias(userId: string, options?: SegmentOpts, callback?: () => void): void;

    /* trackLink is a helper that binds a track call to whenever a link is
       clicked. Usually the page would change before you could call track, but
       with trackLink a small timeout is inserted to give the track call enough
       time to fire. */
    trackLink(
      elements: JQuery | Array<Element> | Element,
      event: string | { (elm: Element): string },
      properties?: Object | { (elm: Element): Object }
    ): void;

    /* trackForm is a helper that binds a track call to a form submission.
       Usually the page would change before you could call track, but with
       trackForm a small timeout is inserted to give the track call enough
       time to fire. */
    trackForm(
      elements: JQuery | Array<Element> | Element,
      event: string | { (elm: Element): string },
      properties?: Object | { (elm: Element): Object }
    ): void;

    /* The ready method allows you to pass in a callback that will be called as
       soon as all of your enabled integrations have loaded. It’s like jQuery’s
       ready method, except for integrations. */
    ready(callback: () => void): void;

    /* If you need to clear the user and group id and traits we’ve added a
       reset function that is most commonly used when your identified users
       logout of your application. */
    reset(): void;

    /* Once Analytics.js loaded, you can retrieve information about the
       currently identified user or group like their id and traits. */
    user(): {
      id(newId?: string | null): string | null | undefined;
      logout(): void;
      reset(): void;
      anonymousId(newId?: string): string;
      traits(newTraits?: Object): void;
    };

    group(): {
      id(): string;
      traits(newTraits?: Object): void;
    };

    /* Analytics.js has a debug mode that logs helpful messages to the
       console. */
    debug(state?: boolean): void;

    /* The global analytics object emits events whenever you call alias, group,
       identify, track or page. That way you can listen to those events and run
       your own custom code. */
    on(
      event: string,
      callback: {
        (event: string, properties: Object, options: SegmentOpts): void;
      }
    ): void;

    /* You can extend the length (in milliseconds) of the method callbacks and
       helpers */
    timeout(milliseconds: number): void;
  }
}

declare let analytics: SegmentAnalytics.AnalyticsJS;

declare module '@segment/analytics.js-core' {
  var analytics: SegmentAnalytics.AnalyticsJS;
  export default analytics;
}

declare global {
  // eslint-disable-next-line
  interface Window {
    analytics: SegmentAnalytics.AnalyticsJS;
  }
}

I'm not sure I did it right, because when I try and use it, I get a different error now:

import '../segment-analytics.d';
// ...
window.analytics.addDestinationMiddleware('Pendo', [AnalyticsPendoMiddleware]);

It shows:

Property 'analytics' does not exist on type 'Window & typeof globalThis'.ts(2339)
Alien
  • 944
  • 2
  • 8
  • 22

0 Answers0