javascripttypescriptdeclare

How to declare experimental global variables in TypeScript?


I need to use the EyeDropper API in my TypeScript project. TypeScript compiler doesn't know anything about this API, so I declared it myself:

interface ColorSelectionResult {
    sRGBHex: string;
}

interface ColorSelectionOptions {
    signal: AbortSignal;
}

declare class EyeDropper {
    open(options?: ColorSelectionOptions): Promise<ColorSelectionResult>;
}

export default EyeDropper;

It works great, however I'm not sure how to properly check if this API is handled by the browser in the runtime:

if (typeof EyeDropper === "function") {
    // EyeDropper is available
}

This would work, but I'd need to remember about the check. Instead, I'd prefer the TypeScript engine remember that it's either a class, or undefined. I tried to create a type:

type EyeDropper = EyeDropperClass | undefined;

export default EyeDropper;

But then, I can't use this type to explicitly check if EyeDropper is available. I tried this:

if (window.EyeDropper === "function") {
    // Property 'EyeDropper' does not exist
    // on type 'Window & typeof globalThis'.ts(2339)
}

Instead, this would work:

if ("EyeDropper" in window) {
    // EyeDropper is available
}

But this time, it's a little hacky and I'd prefer to do the check via type, not string.

How should it be done in TypeScript?


Solution

  • In Typescript, typeof operator is used for values - not really meant for types.

    The best way of going about this is to use type augmentation to add new property to the Window interface. This means that the property would have the type of EyeDropper or undefined, meaning that the API may or may not exist.

    In the global type definition file (typically named globals.d.ts), you can add:

    declare global {
        interface Window {
            EyeDropper?: EyeDropper;
        }
    }
    

    With your actual code, you can use a type-guard function to check if the EyeDropper API exists:

    function isEyeDropperAvailable(): window.EyeDropper is EyeDropper {
        return typeof window.EyeDropper === "function";
    }
    
    if (isEyeDropperAvailable()) {
        // EyeDropper is available
    }