Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Service Worker typings #11781

Open
jgw96 opened this issue Oct 21, 2016 · 33 comments
Open

Service Worker typings #11781

jgw96 opened this issue Oct 21, 2016 · 33 comments
Labels
Domain: lib.d.ts The issue relates to the different libraries shipped with TypeScript Help Wanted You can do this Suggestion An idea for TypeScript
Milestone

Comments

@jgw96
Copy link

jgw96 commented Oct 21, 2016

TypeScript Version: 2.0.3

Types for Service Workers should be built into the default definitions for TypeScript. There are some good examples out there of Service Worker typings from the community.

@mhegazy mhegazy added Domain: lib.d.ts The issue relates to the different libraries shipped with TypeScript Suggestion An idea for TypeScript labels Oct 21, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Oct 21, 2016

Since service workers run in their own context, i suppose we need --lib serviceworker (similar to --lib webworker).

@zhengbli can we update our declaration files to pull in serviceworker spec idl as well

@idiotWu
Copy link

idiotWu commented Jan 1, 2017

I think it's necessary to add service worker typings into built-in definitions, though it's still a work-in-progress API.

When I tried to add /// <reference no-default-lib="true"/> directive into service_worker_api to exclude the default lib (like that in lib.webworker.d.ts), I got tons of type missing errors like:

 error TS2304: Cannot find name 'Function'.

So instead I have to use

interface Window extends ServiceWorkerGlobalScope {}

to attach interfaces onto self.

This brings some other problems, eg. I must change the incompatible properties like onmessage into what they are on Window interface:

// expected
interface ServiceWorkerGlobalScope {
    onmessage: (messageevent: ExtendableMessageEvent) => void;
}

// now
interface ServiceWorkerGlobalScope {
    onmessage: (messageevent: MessageEvent) => void;
}

Therefore, I think it would be much better to include service worker typings in default definitions, or, at least there should be a way to make no-default-lib directive act like the built-in ones.

@code-tree
Copy link

interface Window extends ServiceWorkerGlobalScope {} isn't even working for me.

Anyone know how I can force TS to change the type of self??

@mhegazy
Copy link
Contributor

mhegazy commented Jan 25, 2017

Anyone know how I can force TS to change the type of self??

first you need --lib webworker to include the webworker library definition. self is defined as declare var self: WorkerGlobalScope;. so update the declaration of WorkerGlobalScope to get what you want.

@rektide
Copy link

rektide commented Jun 16, 2017

I can't vouch for it, but scouting about it looks like this gist is updating pretty regularly. May be a good candidate for TS to adopt?

@michaeljota
Copy link

I really don't like this type of comments, but... Any updates here? I mean, this is just a definition file with a proper compiler flag, isn't it? Maybe we won't be writing service workers in Typescript, but Typescript definitions are really important now days in Javascript development, for me at least. :P.

@mhegazy mhegazy added the Help Wanted You can do this label Apr 3, 2018
@mhegazy
Copy link
Contributor

mhegazy commented Apr 3, 2018

PRs welcomed. the files are generated https://github.com/Microsoft/TSJS-lib-generator. I would be happy to help if someone wants to pick this issue up.

@mhegazy mhegazy added this to the Community milestone Apr 3, 2018
@tiernan
Copy link

tiernan commented Apr 13, 2018

I wrote an updated gist from the one linked above by @rektide here: Service Worker Typings to Supplement lib.webworker.d.ts.

It's intended to be included by a triple slash directive in the service worker, and registration scripts. It works well for me using the webworker lib along with es5+. Hope this helps

@caseyhoward
Copy link

I feel like I'm completely missing something. I tried a couple of the type definition files and they're pretty much missing everything I need. addEventListener isn't declared to take a callback that takes a ServiceWorkerMessageEvent. And ServiceWorkerMessageEvent doesn't have the properties I'm using (request, waitUntil, respondWith). self is missing everything since it's still defined as a WorkerGlobalScope.

And to further add to my confusion this says that the dom lib now has service worker stuff: https://www.npmjs.com/package/@types/service_worker_api Why would that be? I didn't think there was a DOM inside service workers. Also, I tried it and it didn't work.

Is there something else I need to configure or include somewhere?

@jhpratt
Copy link

jhpratt commented Dec 3, 2018

Pending an update on this, I've just switched my service worker code into vanilla JS. Though types would be great for development, this is a basic failing of TypeScript currently.

@shqld
Copy link

shqld commented Dec 23, 2018

I'm addressing the issue like this. Currently works well.

https://gist.github.com/shqld/32df51a4a4ed429f2c76e4e2cfdf6f96#gistcomment-2793376

@shqld
Copy link

shqld commented Dec 23, 2018

And created a package https://github.com/shqld/types-serviceworker

(If this violates licenses, please tell me)

@13rac1
Copy link

13rac1 commented Jan 15, 2019

@caseyhoward The PR removing Service Worker API is DefinitelyTyped/DefinitelyTyped#14786 Agreed, the "dom lib" message is incorrect. I got it seemingly working with --lib es6,webworker as explained in #14877 (comment)

@shqld Thanks for this! Edit: I've tried both methods. Many hacks are required to use the webworker library and types-serviceworker just works.

@hsource
Copy link

hsource commented Jun 19, 2019

For anyone else who comes upon this thread, there seems to be 2 main solutions:

  1. Use the https://github.com/shqld/types-serviceworker package by @azarashd, with the triple-slash directives to include the typings. Note that you can only use these if you don't have { "lib": ["dom"] } declared

  2. Follow this StackOverflow reply by Jake Archibald (one of the main Chrome engineers who worked on Service Workers) to break your project into multiple tsconfig files. One of the tsconfig files will only include the { "lib": ["webworker"] } for the Service Worker and one will only include { "lib": ["dom"] } for the normal site. The file also needs to declare var self: ServiceWorkerGlobalScope; export {}; to fix the scope as a hack

A few additional notes:

I had a setup where the broader project had many typings installed via @types/*. By default, all of these typings are included. However, many typings assume that your tsconfig includes { "lib": ["dom"] } already and return type errors without them.

To ensure that this works, make sure you include { "types": [] } to not include the typings by default. Instead, only include typings you use in the tsconfig project with the Service Worker.

@simon-robertson
Copy link

It would make things much easier if we could declare a script's environment with triple-slash directives. These could override any configuration that is being used.

/// env node
/// env worker
/// env serviceworker

ESLint provides a similar solution.

/* eslint-env node */
/* eslint-env worker */
/* eslint-env serviceworker */

Most of the development teams that I know tend to push TS scripts through Babel and Webpack, and need a simple way to target multiple environments in one TS code base so VSCode (and friends) can provide the correct APIs (code hints etc).

@simon-robertson
Copy link

The way the service worker issue was resolved here was by adding "webworker" to the tsconfig lib array, and then wrapping the service worker code in a function.

const initialize = (service: ServiceWorkerGlobalScope): void => {
    service.addEventListener("install", ... )
}

initialize(self as any)

Not ideal, because the service worker APIs are now picked up by all of the scripts in the project, but at least this way we have access to the service worker type definitions.

@zbjornson
Copy link

It would make things much easier if we could declare a script's environment with triple-slash directives. These could override any configuration that is being used.

@simon-robertson you can, see https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-lib-

/// <reference lib="webworker" /> 

@msvinth
Copy link

msvinth commented Apr 15, 2020

It would make things much easier if we could declare a script's environment with triple-slash directives. These could override any configuration that is being used.

@simon-robertson you can, see https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-lib-

/// <reference lib="webworker" /> 

I did the following and it worked for my needs e.g.:

/// <reference no-default-lib="true"/>
/// <reference lib="es2015" />
/// <reference lib="webworker" />

interface Window extends ServiceWorkerGlobalScope {}

self.addEventListener("fetch", (event: FetchEvent) =>  {
    event.respondWith(caches.match(event.request));
});

@simon-robertson
Copy link

I did the following and it worked for my needs e.g.:

/// <reference no-default-lib="true"/>
/// <reference lib="es2015" />
/// <reference lib="webworker" />

interface Window extends ServiceWorkerGlobalScope {}

self.addEventListener("fetch", (event: FetchEvent) =>  {
    event.respondWith(caches.match(event.request));
});

Short of breaking up a project into multiple workspace projects, I guess that would be the best solution for now. I was thinking of something more along the lines of ...

/// <environment type="dedicatedworker" /> or
/// <environment type="serviceworker" /> or
/// <environment type="audioworklet" /> and so on

Essentially overriding the libraries specified in the tsconfig.json file for isolated use cases.

@iiic
Copy link

iiic commented Jun 4, 2020

Which lib I need for InstallEvent ?

…videlicet…

I added "webworker" into local config json and it perfectly worked with ServiceWorkerGlobalScope ExtendableEvent PushEvent SyncEvent , … but still have error (2304) in InstallEvent

InstallEvent code looks like:

sw.addEventListener( 'install', function ( /** @type {InstallEvent} */ event )
{
	//…
});

where sw is instance of ServiceWorkerGlobalScope

Someone understand it? Do I missing some lib for InstallEvent?

@grundmanise
Copy link

@iiic #14877 (comment)

@simon04
Copy link

simon04 commented Dec 23, 2020

For JavaScript, the following snippet helped to bring IntelliSense to Visual Studio Code by making use of JSDoc:

// service-worker.js

/// <reference no-default-lib="true"/>
/// <reference lib="es2015" />
/// <reference lib="webworker" />

/**
 * @type {ServiceWorkerGlobalScope}
 */
const sw = self;

sw.addEventListener("install", event => {
  console.log(event);
  console.log(sw.caches);
  console.log(sw.registration);
});

@michaeljota
Copy link

michaeljota commented Jan 1, 2021

@simon04 Just it, and you don't even have to alias self. You can see it here:

TS Play

@googol
Copy link

googol commented Feb 24, 2021

Not aliasing self works if you are writing a web worker, but to use the service worker events like install, you can't just use self out of the box. These events are only defined in the ServiceWorkerGlobalScope interface.

What I ended up using today is:

const sw: ServiceWorkerGlobalScope & typeof globalThis = self as any

alongside separate tsconfigs that specify different libs and includes (and extend a common base).

@jhony1104
Copy link

jhony1104 commented Mar 22, 2021

I don't know if this is a good idea, but one could, instead of aliasing self, change the typing of self with something like:

declare var self: ServiceWorkerGlobalScope & typeof globalThis;

@HolgerJeromin
Copy link
Contributor

I don't know if this is a good idea, but one could, instead of aliasing self, change the typing of self with something like:

At least my TypeScript 4.0 setup is not happy:

1>CacheManagement\ServiceWorker.ts(6,13): error TS2403: Build:Subsequent variable declarations must have the same type.  
Variable 'self' must be of type 'WorkerGlobalScope & typeof globalThis', but here has type 'ServiceWorkerGlobalScope & typeof globalThis'.

@Glandos
Copy link

Glandos commented Jul 1, 2021

With TypeScript 4.3, I now have the following:

/// <reference no-default-lib="true"/>
/// <reference lib="es2020" />
/// <reference lib="WebWorker" />

const sw = self as ServiceWorkerGlobalScope & typeof globalThis

The no-default-lib is needed to workaround the fact that self is also a Window and not castable.
Of course, now, you must use sw and not self 😞

nna774 added a commit to nna774/status.nna774.net that referenced this issue Oct 22, 2021
@jimmywarting
Copy link
Contributor

for jsdoc and vanilla js developers:

/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />
/// <reference lib="WebWorker" />
const sw = /** @type {ServiceWorkerGlobalScope & typeof globalThis} */ (globalThis)

@Mlocik97
Copy link

Mlocik97 commented May 3, 2022

jimmywarting thanks for that solutions, even tho, it gave me another error, now from ESLint:

'globalThis' is not defined.eslint[no-undef](https://eslint.org/docs/rules/no-undef)

as well as TypeScript gave error:

Conversion of type 'Window & typeof globalThis' to type 'ServiceWorkerGlobalScope & typeof globalThis' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
  Type 'Window & typeof globalThis' is missing the following properties from type 'ServiceWorkerGlobalScope': clients, onactivate, onfetch, oninstall, and 5 more.ts(2352)

So I did this:

/// env serviceworker
const globalThis = /** @type {unknown} */ (self);
/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />
/// <reference lib="WebWorker" />
const sw = /** @type {ServiceWorkerGlobalScope & typeof globalThis} */ (globalThis)

yeah, that's crazy.

Offroaders123 added a commit to Offroaders123/Smart-Text-Editor that referenced this issue Oct 15, 2022
Rewrote the Service Worker in TypeScript! It *kind of* works great, but not quite. I wish the TypeScript team made tooling for Workers a bit better, as it seems harder to work with than it should be.

This rewrite was all from last night, and it took a few hours to learn how to get things right (I didn't get around to commiting it until now though). There aren't any type errors anymore, but I'm also unsure if it will run sucessfully yet. I don't have a device/OS to test the Web Share Target API support anymore, since my Chromebook runs Ubuntu now :)

While it's not fully functional yet, this is a step in the right direction for type-safe Service Worker code!

I mostly rewrote it with my new programming styles, expanding things out for readability, and making use of async-await where it was previously just `.then()` calls and such.

Some help to get it working:
https://github.com/NicholasPeretti/service-worker-ts-example
https://stackoverflow.com/questions/89332/how-do-i-recover-a-dropped-stash-in-git (I had an oopsie XD)

Edit:
Tried testing out the worker in the browser, and modern TypeScript now includes empty `export {}` instances in the resulting JS, which breaks declaring the file as a module, as you can't have `export {}` in a non-module script. The only way I got close to this was to name the file `.mts`, but I'm not sure if I like that or not. But hey, it may work. With that being a possible solution, I found all of these sources along the way also:
https://stackoverflow.com/questions/56356655/structuring-a-typescript-project-with-workers
https://github.com/jakearchibald/typescript-worker-example (now outdated, unfortunately, thanks to this new TypeScript change)
microsoft/TypeScript#41513
https://www.devextent.com/create-service-worker-typescript/ (almost works! falls down to the same issue sadly)
microsoft/TypeScript#14877
microsoft/TypeScript#11781
@alexbruno
Copy link

alexbruno commented Jan 3, 2023

The simplest solution that worked for me, is just this on the Service Worker code:

/// <reference lib="WebWorker" />

const sw: ServiceWorkerGlobalScope = self as any

@F2
Copy link

F2 commented Feb 26, 2023

To avoid aliasing self, this seems to work in the latest version of TypeScript:

/// <reference lib="WebWorker" />

declare let self: ServiceWorkerGlobalScope;

@ShafSpecs
Copy link

To avoid aliasing self, this seems to work in the latest version of TypeScript:

/// <reference lib="WebWorker" />

declare let self: ServiceWorkerGlobalScope;

It gives an error:

Cannot redeclare block-scoped variable 'self'

Using this instead worked:

/// <reference lib="WebWorker" />

export type {};
declare let self: ServiceWorkerGlobalScope;

Yep, it's getting weirder 😆

@icazemier
Copy link

This works for me (for now... 🤷🏻 ):

/// <reference lib="esnext" />
/// <reference lib="webworker" />
/// <reference no-default-lib="true"/>

// service-worker.ts
(() => {

  const initializeSW = (sw: ServiceWorkerGlobalScope) => {
    sw.addEventListener('install', (event: ExtendableEvent) => {
      // eslint-disable-next-line no-console
      console.log(event);
    });

    // see: https://developer.mozilla.org/en-US/docs/Web/API/Clients/claim
    sw.addEventListener('activate', (event: ExtendableEvent) => {
      // eslint-disable-next-line no-console
      console.log(event);
      event.waitUntil(sw.clients.claim());
    });

  };

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  initializeSW(self as any);
})();

tsconfig:

{
  "compilerOptions": {
    "allowJs": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true,
    "strictNullChecks": true,
    "useUnknownInCatchVariables": false,
    "lib": ["WebWorker", "ESNext"],
    "noEmit": true,
    "strict": true,
    "outDir": "./public/sw",
    "module": "esnext",
    "target": "esnext",
    "incremental": false,
    "sourceMap": true,
    "isolatedModules": false,
    "moduleResolution": "node",
    "resolveJsonModule": true
  },
  "include": ["service-worker.ts"],
  "exclude": ["node_modules"]
}
"typescript": "^5.0.2",

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Domain: lib.d.ts The issue relates to the different libraries shipped with TypeScript Help Wanted You can do this Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests