1

I'm rewriting ES5 JavaScript code into TypeScript and have code like this:

var is_browser = (function() {
    try {
        return this === window;
    } catch (e) {
        return false;
    }
})();

First thing is that I've got an error from TS that this is implicit of type any. But I don't think it matters much because the code will not work since TypeScript is using "use strict" for every file, so this will be undefined.

So what is the proper way to test if JavaScript code runs in the browser?

jcubic
  • 61,973
  • 54
  • 229
  • 402
  • can't you write this part in javascript, and just add file declaration in typescript ? – Ahmed Lazhar Aug 06 '22 at 16:24
  • 1
    or you can use `globalThis.window === globalThis` – Ahmed Lazhar Aug 06 '22 at 16:25
  • 2
    The existence of the variable `window` itself indicates that you are in a browser. What that code is doing is exactly this, it's dereferencing `window` and if it throws, then the variable doesn't exist and you're not in a browser – Christian Vincenzo Traina Aug 06 '22 at 16:26
  • 1
    you could also check if window is not undefined: `typeof window !== 'undefined'` – MauricioRobayo Aug 06 '22 at 16:28
  • @CristianTraìna sure but what about jsDOM I want to detect the browser, not the window object. – jcubic Aug 06 '22 at 16:39
  • @MauricioRobayo This will break in multiple ways example in jsDOM in Node that also has a window object. – jcubic Aug 06 '22 at 16:40
  • @AhmedLazhar I think that this is the right way. Thanks. You can add an answer with this solution. – jcubic Aug 06 '22 at 16:41
  • @jcubic Thanks but I think MauricioRobayo solution should be accepted since it works with all browsers, `globalThis` don't work on old browsers check this link: https://caniuse.com/?search=globalThis – Ahmed Lazhar Aug 06 '22 at 16:45
  • Which features of the browser do you intend to use? Simply checking for `window` doesn't guarantee that the other features that you want are there. (e.g. [Deno](https://deno.land/) has `globalThis.window`, but it's not a browser). You should feature-detect each API that you intend to use. – jsejcksn Aug 06 '22 at 17:31
  • @AhmedLazhar I specifically written a proper way, typeof is not a proper way, which may break in jsDOM. That's why I've written a hack with `this` I want bulletproof code, not something that can break anytime. I'm working on a JavaScript library that can be used in different places. – jcubic Aug 06 '22 at 20:08

2 Answers2

3

This solution should always work however the global context is polluted

var is_browser = Object.getPrototypeOf(
  Object.getPrototypeOf(globalThis)
) !== Object.prototype
Ahmed Lazhar
  • 718
  • 4
  • 10
  • This is exactly what I was looking for so that it works in NodeJs even when someone manually defines `window` globally – lwdthe1 Mar 26 '23 at 22:53
1

If you are on the browser window should not be undefined:

var is_browser = typeof window !== 'undefined';

console.log(is_browser)
MauricioRobayo
  • 2,207
  • 23
  • 26
  • This will break `var windows = 10;` Also it will break when testing in jest framework that use jsDOM. – jcubic Aug 06 '22 at 16:37
  • fair point @jcubic, thanks for the feedback. Although your question was titled "How to **detect if code runs in browser** in TypeScript?" And you explicitly asked "what is the proper way to test if JavaScript code runs **in the browser**?". You never mentioned Jest or jsDOM. Granted, window could be defined by someone else, same as globalThis. – MauricioRobayo Aug 06 '22 at 16:46
  • window can always be defined to point to globalThis – Ahmed Lazhar Aug 06 '22 at 16:49
  • Sure but this is not the answer I've expected that you can learn in 1 month of learning JavaScript. I wanted something that will work everywhere. Maybe I should write that in the question. But it's kind of silly to add please no answer with `typeof window`. Maybe I should from now on writing a list of answers that I don't want to see. – jcubic Aug 06 '22 at 20:12
  • Yeah, sometimes is hard to do the simple thing. https://youtu.be/AkYDsiRVqno?t=1037 – MauricioRobayo Aug 08 '22 at 03:34