12

How does one make this work in Typescript?

window.open(externalUrl, '_blank').focus();

this throws the following typescript error:

Object is possibly 'null'.ts(2531)

I have tried this, but it does not work:

if (typeof window != 'undefined' && window && externalUrl !== '' && window.open(externalUrl, '_blank') ) {
      window.open(externalUrl, '_blank').focus();
    }
psvj
  • 8,280
  • 8
  • 30
  • 44
  • window object is only available in browser. – Markus Zeller Jan 22 '20 at 17:16
  • what does `open(...)` returns ? – Nicolas Jan 22 '20 at 17:16
  • 2
    `window.open(...)?.focus()` - "optional chaining" operator `?.` – Kyle Pittman Jan 22 '20 at 17:17
  • @MarkusZeller that should be taken care of by the if statement, no? – psvj Jan 22 '20 at 17:17
  • Does this answer your question? [How do you explicitly set a new property on \`window\` in TypeScript?](https://stackoverflow.com/questions/12709074/how-do-you-explicitly-set-a-new-property-on-window-in-typescript) – Markus Zeller Jan 22 '20 at 17:17
  • @Monkpit this solution would only work in typescript 3.7+ – Nicolas Jan 22 '20 at 17:18
  • @Nicolas returns an object like this: blur: ƒ () close: ƒ () closed: false focus: ƒ () frames: global {window: global, self: global, location: Location, closed: false, frames: global, …} length: 0 location: Location {} opener: Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} parent: global {window: global, self: global, location: Location, closed: false, – psvj Jan 22 '20 at 17:21
  • 1
    pre 3.7 it could be `const w = window.open(externalUrl, '_blank'); if (w) w.focus();`; the issue is that `window.open()` could return `null` and you need to save its value if you want to check it and then use it; you can't check one call to `window.open()` for `null` and have it imply anything for other calls. – jcalz Jan 22 '20 at 17:22
  • @jcalz this is the answer, submit it as an answer and i will select it--- thanks! – psvj Jan 22 '20 at 17:24
  • @MarkusZeller this isn't really the same problem as the post you suggest Markus, I am not trying to add a new property to the window – psvj Jan 22 '20 at 17:32

2 Answers2

23

Assuming you're using an IDE, there's probably a red squiggly line that shows which part of the expression is the problem:

  window.open(externalUrl, '_blank').focus(); // error
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Object is possibly 'null'

Here, it's saying that window.open(externalUrl, '_blank') is possibly null, not that window itself might be. So you don't have to check window to address this particular error (although depending on the runtime JS environment you're using there may or may not be a window. I'll assume there is one).

Anyway, the error you're seeing comports with the MDN documentation for Window.open(), which says that if the window can't be opened the return value will be null. To address this, you need to look at the return value, and only call focus() on it if it's not null. Note that you can't do it this way:

if (window.open(externalUrl, '_blank')) {
    window.open(externalUrl, '_blank').focus(); // still error
}

That's because the compiler does not believe that one successful call to window.open() implies that a second call will also be successful. As far as the compiler knows, it's possible that the first call returns a Window and the second call returns null. That seems unlikely to me if you're calling it with the same arguments, but I can't say for certain that it would work. And besides, you don't really want to open two windows, do you?


So, how should we do it? A way that works with most version of TypeScript is just to save the result in a variable and test it:

const w = window.open(externalUrl, '_blank');
if (w) {
    w.focus(); // okay now
}

In TypeScript 3.7 and up, there's support for the optional chaining operator, ?.. This lets you write the above more tersely as:

window.open(externalUrl, '_blank')?.focus(); // okay also

Eventually you can expect the optional chaining operator to be part of JavaScript itself; until then, the above code emits to JavaScript as something like:

// JavaScript emitted 
(_a = window.open(externalUrl, '_blank')) === null || _a === void 0 ? void 0 : _a.focus();

which saves the result of window.open() into a variable (like we did with w above) and then only calls focus() on it if it's not null or undefined. Anyway, either way should work for you.


Okay, hope that helps; good luck!

Playground link to code

kaya3
  • 47,440
  • 4
  • 68
  • 97
jcalz
  • 264,269
  • 27
  • 359
  • 360
1

Based on @jcalz's excellent answer, there is also one-liner, using !:

window.open(externalUrl, '_blank')!.focus();

T.Todua
  • 53,146
  • 19
  • 236
  • 237
  • 1
    `!` asserts that the value is not null. That is not the same as passing the `if` check. But there is an equivalent, which would be `window.open(externalUrl, '_blank')?.focus();` In this case `focus()` will only be run if the object is not null. – calvinsomething Aug 10 '22 at 17:05