TypeScript type definitions for ServiceWorker

16,081

Solution 1

Type definitions for ServiceWorker and friends are now available from DefinitelyTyped/service_worker_api:

$ typings install dt~service_worker_api --global --save
service_worker_api
└── (No dependencies)

Usage example:

// data-access.service.ts
/// <reference path="typings/globals/service_worker_api/index.d.ts" />
import { Injectable } from '@angular/core';

@Injectable()
export class DataAccess {
    constructor() {
        navigator.serviceWorker.register('service-worker.js', { scope: '/api/' });
    }
}

Adjust the paths as necessary.

Solution 2

ServiceWorker is not a chrome specific extension.

OP should refer to Jake Archibald's isSERVICEWORKERready? page for a rundown of the current state of ServiceWorker in the popular browsers.

I added the type definitions in tsd.d.ts based on the interface information linked by OP and they seem to be working.

Please note that I referenced IPromise interface from angular.d.ts.

Defined in tsd.d.ts

/// <reference path="angularjs/angular.d.ts" />

interface Navigator {
    serviceWorker: ServiceWorkerContainer;
}

interface ServiceWorkerContainer {
  register(scriptUrl: string, options?: RegistrationOptions): angular.IPromise<ServiceWorkerRegistration>;
}

interface RegistrationOptions {
  scope: string;
}

interface ServiceWorkerRegistration {
  installing?: ServiceWorker;
  waiting?: ServiceWorker;
  active?: ServiceWorker;

  scope: string;

  update(): angular.IPromise<void>;
  unregister(): angular.IPromise<boolean>;
}

interface ServiceWorker {
  scriptUrl: string;
  state: string;

  postMessage(message: any, transfer: Array<any>): void;
}

Referred in home-controller.ts

///<reference path='../../typings/tsd.d.ts' />
...
// dependencies are injected via AngularJS $injector
constructor() {
  var vm = this;
  vm.ctrlName = 'HomeCtrl';
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.js', { scope: '/' }).then(function(reg) {
      // registration worked
      console.log('Registration succeeded. Scope is ' + reg.scope);
    }).catch(function(error) {
      // registration failed
      console.log('Registration failed with ' + error);
    });
  } else {
    console.warn('serviceWorker not available in navigator.');
  }
}

Building and loading the app in the chrome

enter image description here

logs the following console message:

enter image description here

Hope this helps.

Solution 3

You can add to the interface in your TypeScript file and when the lib.d.ts is updated, the compiler will tell you that you no longer need it.

interface Navigator {
    getUserMedia(
        options: { video?: bool; audio?: bool; }, 
        success: (stream: any) => void, 
        error?: (error: string) => void
        ) : void;
}

navigator.getUserMedia(
    {video: true, audio: true}, 
    function (stream) {  },
    function (error) {  }
);

or

Instead of changing the definition you can cast to an any object an call with arbitray parameters e.g:

 var n = <any>navigator;
    n.getUserMedia  = n.getUserMedia || n.webkitGetUserMedia || n.mozGetUserMedia || n.msGetUserMedia;
    return  n.getUserMedia({video: true, audio:true}, onSuccess, onFail);

Solution 4

Original Answer

Here are the Typings I created to use with WebStorm: Gist.

Then in my service-worker.ts file I include a reference: /// <reference path="YOUR_PATH_HERE/service-worker.d.ts" />.

Adjust your path accordingly, or make them global and you should be pretty set. It's fairly complete, but I'm sure it's missing some things. Anyway, I hope this helps.

2018 Update:

I published a new edition of these definitions to work with lib.webworker.d.ts along with any es5+ library (lib.es5.d.ts, lib.es2015.d.ts, etc) here: Service Worker Typings to Supplement lib.webworker.d.ts. This set should be much more relevant today.

Share:
16,081
James B
Author by

James B

Updated on July 19, 2022

Comments

  • James B
    James B almost 2 years

    I'm writing a service for my angular 2 app using TypeScript. The service makes use of chrome's ServiceWorker to listen for push notifications (see tutorial). The code (javascript) makes use of navigator first to see if serviceWorker is supported, then continues to complete registration etc., i.e.,

    if ('serviceWorker' in navigator) {
      console.log('Service Worker is supported');
      navigator.serviceWorker.register('sw.js').then(function() {
        return navigator.serviceWorker.ready;
      }).then(function(reg) {
        console.log('Service Worker is ready :^)', reg);
          // TODO
      }).catch(function(error) {
        console.log('Service Worker error :^(', error);
      });
    }
    

    I'd like to implemenet the above using TypeScript. However, the current lib.d.ts used by the TypeScript compiler (see below) appears to have no definitions defined on Navigator for the serviceWorker or its associated methods such as serviceWorker.register (I guess since it's a chrome-specific implementation).

    interface Navigator extends Object, NavigatorID, NavigatorOnLine, NavigatorContentUtils, NavigatorStorageUtils, NavigatorGeolocation, MSNavigatorDoNotTrack, MSFileSaver, NavigatorUserMedia {
        readonly appCodeName: string;
        readonly cookieEnabled: boolean;
        readonly language: string;
        readonly maxTouchPoints: number;
        readonly mimeTypes: MimeTypeArray;
        readonly msManipulationViewsEnabled: boolean;
        readonly msMaxTouchPoints: number;
        readonly msPointerEnabled: boolean;
        readonly plugins: PluginArray;
        readonly pointerEnabled: boolean;
        readonly webdriver: boolean;
        getGamepads(): Gamepad[];
        javaEnabled(): boolean;
        msLaunchUri(uri: string, successCallback?: MSLaunchUriCallback, noHandlerCallback?: MSLaunchUriCallback): void;
        requestMediaKeySystemAccess(keySystem: string, supportedConfigurations: MediaKeySystemConfiguration[]): PromiseLike<MediaKeySystemAccess>;
        vibrate(pattern: number | number[]): boolean;
        addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
    }
    

    The result is that I'm faced with compilation errors since the compiler can't find the associated serviceWorker types. Given I'm new to JavaScript and TypeScript, I'm trying to determine the best way to proceed. I understand the options to be:

    1. Keep the js code and simply ignore the compilation errors (not ideal).
    2. Keep the js code and somehow suppress the type errors during compilation.
    3. Find an existing typescript definitions library that has serviceWorker defined and include that during compilation.
    4. Write my own typescript definition file for navigator or somehow extend the existing lib.d.ts

    Sage advice on the best option greatly appreciated.

    Update

    Attempted to cast to any to remove compilation errors, i.e.,

    var nav = <any> navigator;
    
        if ('serviceWorker' in nav) {
           nav.serviceWorker.register('sw.js')
               .then(function(reg) {
                        console.log('yey!', <any> reg);
               }).catch(function(err) {
                    console.log('boo!', <any> err);
        });
    

    but now facing new errors, i.e.,

    error TS7006: Parameter 'reg' implicitly has an 'any' type.
    error TS7006: Parameter 'error' implicitly has an 'any' type.
    

    Also, tempted to write definitions for the ServiceWorker using these details. However never done it before, so will need some practice!