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

Unable to access ServiceWorkerGlobalScope via self. #14877

Open
vincentbel opened this issue Mar 27, 2017 · 35 comments
Open

Unable to access ServiceWorkerGlobalScope via self. #14877

vincentbel opened this issue Mar 27, 2017 · 35 comments
Labels
Bug A bug in TypeScript Domain: lib.d.ts The issue relates to the different libraries shipped with TypeScript Help Wanted You can do this
Milestone

Comments

@vincentbel
Copy link

Since ServiceWorker related types sit in lib.webworker.d.ts, webworker lib need to be added to tsconfig.json.

{
  "compilerOptions": {
    "lib": [
      "es2017",
      "webworker"
    ]
  }
}

But in current lib.webworker.d.ts, it declare var self: WorkerGlobalScope;, so we can't access ServiceWorkerGlobalScope via self.


TypeScript Version: 2.2.1

Expected behavior:

Can access ServiceWorkerGlobalScope via self.(like self.clients)

Actual behavior:

Access with error.

image

@mhegazy
Copy link
Contributor

mhegazy commented Mar 27, 2017

I have not found references in the sepec that self is of type ServiceWorkerGlobalScope.

I think clients should be available as a global variable though. i.e.:

declare var clients: Clients;

@mhegazy mhegazy added Bug A bug in TypeScript Domain: lib.d.ts The issue relates to the different libraries shipped with TypeScript Help Wanted You can do this labels Mar 27, 2017
@mhegazy
Copy link
Contributor

mhegazy commented Mar 27, 2017

PRs welcomed. You can find more information about contributing lib.d.ts fixes at https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md#contributing-libdts-fixes.

@YuichiNukiyama
Copy link
Contributor

self property will return browsing context object. And according to MDN, sometimes return ServiceWorkerGlobalScope object. See following docs.

@vincentbel
Copy link
Author

I'm wondering whether we need to separate typings of web worker and service worker for the following reasons:

  1. If we put all worker typings into one lib, How can we prevent web worker user using service worker methods and properties like clients.
  2. close is not accessible in service worker, how can we omit close in ServiceWorderGlobalScope?
  3. We can access service worker methods and properties like clients registration via self in Chrome 57.(Just like the self in Window)

image

@YuichiNukiyama
Copy link
Contributor

FYI #11781

@robianmcd
Copy link

As a workaround I am currently doing this

(function (self: ServiceWorkerGlobalScope) {
   //Service worker contents...
})(<ServiceWorkerGlobalScope>self);

Anyone have a better solution?

@Havret
Copy link

Havret commented Jun 2, 2017

Any progress about this? It's kinda sad that we cannot use Service Workers alongside Type Script.

@mohsen1
Copy link
Contributor

mohsen1 commented Jun 28, 2017

I did this:

const { clients, addEventListener, skipWaiting, registration } = self as ServiceWorkerGlobalScope;

Note you need "webworker" lib with 2.4

@abarisain
Copy link

Adding declare var self: ServiceWorkerGlobalScope; on top of my service worker implementation fixes that

@mhegazy mhegazy added this to the Community milestone Sep 5, 2017
@jandsu
Copy link

jandsu commented Sep 29, 2017

@abarisain I came up with your solution as well but it fails with following error

Variable 'self' must be of type 'WorkerGlobalScope', but here has type 'ServiceWorkerGlobalScope'.

How did you configure your tsconfig.json to have it working? Thanks!

The workaround I found was:

const _self = self as ServiceWorkerGlobalScope;

...but you then have to use _self everywhere :(

@abarisain
Copy link

abarisain commented Sep 29, 2017

Are you using the latest typescript version?

Turns out since I commented, my needs evolved: I needed to define new variables on the global scope.
Since my project mixes a lot of environments, I also had to have many sub .tsconfig files.

Here is what works for me:

/// <reference path='../../../node_modules/typescript/lib/lib.es6.d.ts' />
/// <reference path='../../../node_modules/typescript/lib/lib.webworker.d.ts' />
/// <reference path="../../types/contextual/serviceworker-patches.d.ts" />

import { foo } from './bar'
declare var self: MyWorkerGlobalScope;
interface MyWorkerGlobalScope extends ServiceWorkerGlobalScope {
  handleForwardedEvent?: (eventName: string, event: Event) => Promise<void>;
}

serviceworker-patches.d.ts has some other patches to fix the incomplete definitions

here is my tsconfig

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "lib": ["es6"]
  }
}

(the extend part is irrelevant, as all options defined in my base tsconfig are for other stuff)

@jandsu
Copy link

jandsu commented Sep 29, 2017

Thanks for your very fast answer :)

I was stucked with 2.3.4 for other parts of the project but switching to 2.5.2 did not solve my compile error. I am always very confused with the triple-slash thing... and how it relates to adding libs in tsconfig.json

"lib": [
            "webworker",
            "es2016"
        ],

Nevermind, I will remain with my trick... it does the job as well... Hopefully, I will eventually understand those nasty typing issues... not speaking about how to tell the IDE that this file/folder is a service worker while the rest is in the context of Window etc... But this is more a debate for stackoverflow than this issues database.

@abarisain
Copy link

The triple slashes are exactly how I tell that a file is a service worker :) it overrides every "lib" entry, but it's quite a dirty hack.
I think you should isolate your worker in its own subfolder

@Tetr4
Copy link

Tetr4 commented Oct 29, 2017

Here is what worked for me:

  • in tsconfig.json:
{
  "compilerOptions": {
    "lib": [
      "es6",
      "webworker",
      "dom"
    ]
  }
}
  • in the service worker:
import { } from ".";
declare var self: ServiceWorkerGlobalScope;

Not sure why the empty import is required 🤷‍♂️

@Rendalf
Copy link

Rendalf commented May 15, 2018

@Tetr4

Not sure why the empty import is required

I assume to mark file as a module. Without import/export statements TS doesn't mark file as a module.

@benjamingwynn
Copy link

@Tetr4 using an empty import caused an error for me:

Cannot find module '.'.

Adding export default null instead of import { } from "." in the service worker silenced the error.

@michael-hawker
Copy link

@Tetr4 I got errors using your approach as I then got duplicated references between dom and webworker.

I ended up having to build a referenced tsconfig.json with two library configs (kind of like this), which seemed to work ok, but then with export default null at the top, that was copied into my JavaScript output as exports.__esModule and caused errors during registration.

So, I still have some configuration issue, but I'm pretty close.

@wilsonpage
Copy link

I'm having related issues with this.

  • I'm working on a JS project with vscode/typescript checkjs linting.
  • I tried using typings-serviceworker, but it caused all my other non-service-worker files to change scope.
  • I've created a reduced test-case, would be amazing if someone could look at it.

https://github.com/wilsonpage/vscode-scope-test-case

@electrovir
Copy link

Piecing together several comments in here, this is all I ended up needing:

  • Include webworker lib in tsconfig:
{
    "compilerOptions": {
        "lib": ["dom", "webworker"]
    }
}
  • Redeclare self and export null in service worker file:
export default null;
declare var self: ServiceWorkerGlobalScope;

I found multiple config files and installing npm packages not necessary (or even helpful).

@jakearchibald
Copy link

It feels like TypeScript needs to break the various worker types apart in its definitions. This isn't just a service worker issue, it's an issue for other contexts that have a different global, like shared workers & the various worklets.

Either that, or there should be a way to declare which interface is the global.

The various self hacks in this thread only work for references to self. You've still got incorrect types for the global.

fictitious pushed a commit to fictitious/typescript-worker-example that referenced this issue May 29, 2019
microsoft/TypeScript#14877 (comment)

the workaround is to turn service-worker/index.ts into a module so
it has its own scope, then you can redeclare "global" variables with
types that you want
@unadlib
Copy link

unadlib commented Sep 6, 2020

self is used in multiple works, so TypeScript may need to specify in the configuration which files refer to which worker types .

@blixt
Copy link

blixt commented Dec 30, 2020

The workaround described in this issue doesn't appear to work anymore in TypeScript 4.1 because it actually puts the export statement into the build output now, which fails evaluation in the browser.

@oparisblue
Copy link

In TypeScript 4.1+ it seems you can still get it working if you something like this

/// <reference lib="webworker" />
//@ts-ignore
sw=self;
declare var sw: ServiceWorkerGlobalScope;

at the top of your file, and then use sw whenever you need to use self. But it's a very ugly workaround.

@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 😞

@guydumais
Copy link

@Tetr4 Thanks a lot, that fixed it for me but no need to import with import { } from ".";

@soryy708
Copy link

soryy708 commented Nov 26, 2021

Using the lib Triple-Slash Directive /// <reference lib="WebWorker" /> makes ServiceWorkerGlobalScope available

and no-default-lib Triple-Slash Directive /// <reference no-default-lib="true"/> disables the DOM library as Glandos points out:

The no-defaul-lib is needed to workaround the fact that self is also a Window and not castable.

However, if I use these directives in my webworker's source file, other files in my project (and inside node_modules) choke because they can no longer find the DOM stuff (Window, HTMLCollection, document, etc...)

The docs say:

The compiler performs a preprocessing pass on input files to resolve all triple-slash reference directives
The process starts with a set of root files; these are the file names specified on the command-line or in the files list in the tsconfig.json file. These root files are preprocessed in the same order they are specified. Before a file is added to the list, all triple-slash references in it are processed, and their targets included.

Looks like using these directives is global, therefore pretty much equivalent to setting lib in tsconfig.json,
which is unexpected. I expected the Triple-Slash directive to be local to the file in which it is written.

Related:
#20595

Possible solution: https://joshuatz.com/posts/2021/strongly-typed-service-workers/#solution-b-tsconfig-libs

@suiyuex
Copy link

suiyuex commented Mar 3, 2022

Here is a good workaround, with typescript 4.5.5

sw.ts

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

declare const self: ServiceWorkerGlobalScope

@clshortfuse
Copy link

clshortfuse commented Apr 7, 2022

Does anybody have a JS only solution (jsdoc syntax)?

I got as far as:

/// <reference lib="WebWorker" />
/** @type {ServiceWorkerGlobalScope} */
const context = /** @type {unknown} */ (self);

But I'd have to change all references to self. I can't manage to enforce it on self.

/// <reference no-default-lib="true"/>
/// <reference lib="WebWorker" />
/** @type {ServiceWorkerGlobalScope} */
const self = globalThis.self;

Would yield Cannot redeclare block-scoped variable 'self'.ts(2451).

If I try just importing the the lib, It can't find it:

/** @type {import('typescript/lib/lib.webworker').ServiceWorkerGlobalScope} */
const self = globalThis.self;

File '/home/user/project/node_modules/typescript/lib/lib.webworker.d.ts' is not a module.ts(2306)

@soryy708
Copy link

soryy708 commented Apr 7, 2022

@clshortfuse JSDoc isn't related to TypeScript and isn't Microsoft's responsibility.
Regarding your issue with JS only (JSDoc), try asking JSDoc instead.
https://github.com/jsdoc/jsdoc

@clshortfuse
Copy link

@soryy708 You're mistaken. Typescript supports either TS files or JSDocs syntax. In fact, it's part of the official proposal.

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

https://github.com/tc39/proposal-type-annotations

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
aarongable pushed a commit to chromium/chromium that referenced this issue Feb 28, 2023
This CL does a couple of things:

1. Moves the worker script into its own ts_library because it needs
   a different tsconfig.json than the rest of the code. Ideally we would
   be able to use the same target for both, but declarations in the
   lib.webworker.d.ts conflict with lib.dom.d.ts. See
   microsoft/TypeScript#20595

2. The tsconfig.json needs 'webworker' for web worker APIs.

3. In the worker script, add an empty export so we can re-declare
   `self` and re-declare `self` as SharedWorkerGlobalScope so we can
   use shared worker APIs. See
   microsoft/TypeScript#14877

4. Fixes shared worker creation to work with trusted types. We loose
   some type safety when we create the worker because lib.dom.d.ts
   doesn't support Trusted Types yet. See
   microsoft/TypeScript#30024

5. Random fixes for the linter and typescript compilation.

Change-Id: I1d21e67f58b5f3d6b879c21af9e18c18aa6025fd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4289336
Reviewed-by: Zain Afzal <zafzal@google.com>
Commit-Queue: Giovanni Ortuno Urquidi <ortuno@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1110774}
@falco467
Copy link

@clshortfuse

We need to cast to unknown first, to remove the error about incompatible types. It is not very pretty, but at least just two lines overall.
This seems to work today without any errors and full Intellisense:

/* eslint-env serviceworker */
/// <reference lib="webworker" />
const self = /** @type {ServiceWorkerGlobalScope} */(/** @type {unknown} */(globalThis.self))

@madsskoett
Copy link

const self = /** @type {ServiceWorkerGlobalScope} */(/** @type {unknown} */(globalThis.self))

I newer TS version we are still not allowed to redeclare self.
My workaround is: const redeclaredSelf = (self as unknown as ServiceWorkerGlobalScope);
and then using "redeclaredSelf" instead of self.

@jordaaash
Copy link

My current favorite workaround:

(function (this: ServiceWorkerGlobalScope) {
    this.addEventListener('install', async (event) => {
        await this.skipWaiting();
        // ...
    });
}).call(self);

@xaverine
Copy link

Using the lib Triple-Slash Directive /// <reference lib="WebWorker" /> makes ServiceWorkerGlobalScope available

after adding bun-types to qwik project,
ServiceWorkerGlobalScope type lost it's reference and result in TS compile error.

this hint helped me.

@thanhtutzaw
Copy link

declare var self: ServiceWorkerGlobalScope;

Cannot find name 'ServiceWorkerGlobalScope'.ts(2304)
type ServiceWorkerGlobalScope =/unresolved/ any

Offroaders123 added a commit to Offroaders123/Dovetail that referenced this issue Jan 22, 2024
I really like that library, but I think I only want to use in in places I fully have to. Now that my Service Worker is implemented in full plain TS, rather than JSDoc JS, I can use the plain standard library types again. It's still not as simple as just using a plain `<reference>` directive, but all of the types are indeed right!

microsoft/TypeScript#14877
https://github.com/BenjaminAster/Better-TypeScript
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Domain: lib.d.ts The issue relates to the different libraries shipped with TypeScript Help Wanted You can do this
Projects
None yet
Development

No branches or pull requests