Skip to content

Assigning window.location after 5.8.2 upgrade fails #61335

Description

🔎 Search Terms

Unable to assign window.location typescript 5.8.2
error TS2322: Type 'Location' is not assignable to type 'string & Location'.
Type 'Location' is not assignable to type 'string'.

🕗 Version & Regression Information

  • This changed between versions 5.7.3 and 5.8.2

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.8.2#code/O4SwdgJg9sB0A2UDGBDALiKYAEBebokMCy6mYA3AFBA

💻 Code

window.location = window.location;

🙁 Actual behavior

Failing with error:

error TS2322: Type 'Location' is not assignable to type 'string & Location'.
  Type 'Location' is not assignable to type 'string'.

🙂 Expected behavior

Expect it to either be a documented breaking change, OR it should continue to work as it did in 5.7.3:

https://www.typescriptlang.org/play/?ts=5.7.3#code/O4SwdgJg9sB0A2UDGBDALiKYAEBebokMCy6mYA3AFBA

Additional information about the issue

No response

Activity

  1. MartinJohns commented on Mar 4, 2025

    @MartinJohns
    Contributor

    Related: #48949

  2. chrisvltn commented on Mar 4, 2025

    @chrisvltn

    Had the same issue when upgrading to the new version.

    A similar workaround to this one worked for me:

    const win: typeof globalThis = window;
    win.location = window.location; // Pass

    I see the PR #60987 added this part:

    Document.location and Window.location now has stricter get/set types:

    • get location(): Location is unchanged
    • set location(href: string) previously allowed Location (probably because of Typescript's accessor assignability rules)

    Seems to be the cause of the change, as set location expects a string, but globalThis['location'] expects a Location

  3. reifi commented on Mar 5, 2025

    @reifi

    A pain for old style iframe prevention - ts-ignore helps

    if (window.top !== window.self) {
      if (window.top) {  
        // @ts-ignore
        window.top.location = window.self.location;
      }
    }
    
  4. onlywei commented on Mar 13, 2025

    @onlywei

    How can anything satisfy the type string & Location? Was it supposed to be string | Location instead?

  5. clemens commented on Mar 31, 2025

    @clemens

    Independent of whether or not this is a bug/regression: Shouldn't window.location.href = otherWindow.location.href do pretty much what you'd expect in most if not all of the cases?

  6. philwolstenholme commented on Mar 31, 2025

    @philwolstenholme

    This is hitting us in our test files where we will often override window.location to mock it in order to test client-side JavaScript behaviour like this:

    window.location = { ...window.location, pathname: '/modals/main/change-to-delivery' };
    
  7. kurtextrem commented on Apr 8, 2025

    @kurtextrem

    Jake Bailey (@jakebailey) Ryan Cavanaugh (@RyanCavanaugh) sorry for tagging but this is a blocker for some codebases for upgrading to more recent TS versions, unless you add hundreds of ts-ignores. Is a fix planned, should someone make a PR or any other plans (it's been a month and no updates)?

  8. jakebailey commented on Apr 8, 2025

    @jakebailey
    Member

    https://developer.mozilla.org/en-US/docs/Web/API/Window/location says that reading the variable gives a Location object, but that you can write a string to the variable.

    I can imagine that the type being string & Location is itself a problem (probably some known issue with getters/setters; you'll note that the DOM types do not actually have this type anywhere, so it must be created by the checker), it seems to me that window.location = window.location is not actually legal, no?

    Is this only working at runtime due to coercion to string since Location.toString() gives a string? I can't see how window.location = { ...anything } should work, as that wouldn't even have a toString.

    The change came from: microsoft/TypeScript-DOM-lib-generator#1838

    Kagami Sascha Rosylight (@saschanaz)

  9. snarbles2 commented on Apr 8, 2025

    @snarbles2

    Is this only working at runtime due to coercion to string since Location.toString() gives a string? I can't see how window.location = { ...anything } should work, as that wouldn't even have a toString.

    Yes, as far as I can tell from MDN and the spec. Writes to window.location are effectively forwarded to window.location.href, which coerces to a string.

  10. saschanaz commented on Apr 8, 2025

    @saschanaz
    Contributor

    window.location = window.location is effectively window.location.href = window.location.toString(). Which works on browsers, but only with two magics under the hood:

    1. location.href: The window.location setter is overriden so that the value goes to window.location.href instead.
    2. location.toString(): Given that location.href expects string, any passed value will be implicitly stringified.

    The first magic is what's supported by microsoft/TypeScript-DOM-lib-generator#1838. For the second magic, I think in general implicit stringification is not supported, if you want to pass a string then you should explicitly do so.

    And window.location = window.location is just weird, maybe do location.reload() instead? If you feel strongly about the current code, you can do window.location.href = window.location.href which includes no magic.

  11. saschanaz commented on Apr 8, 2025

    @saschanaz
    Contributor

    But the other part of me says maybe we should be less strict and allow passing Location where URLs are expected, as done in microsoft/TypeScript-DOM-lib-generator@2bffd44.

  12. Shelagh-Lewins commented on Apr 28, 2025

    @Shelagh-Lewins

    I note that this code gives a TypeScript error, which doesn't seem like it can be intentional?
    window.location = window.location;

  13. tomh-t commented on May 8, 2025

    @tomh-t

    If you're here because you're stubbing some window location properties - you can bypass the assignment type check:

    For example with a jest stub:

    const oldWindowLocation = window.location;
    
    Object.defineProperty(window, 'location', {
      configurable: true,
      value: Object.defineProperties(
        {},
        {
          ...Object.getOwnPropertyDescriptors(oldWindowLocation),
          assign: {
            configurable: true,
            value: jest.fn(),
          },
      ),
    });
    
    // do stuff... and then
    Object.defineProperty(window, 'location', {
      configurable: true,
      value: oldWindowLocation,
    });
  14. commadelimited commented on Jun 26, 2025

    @commadelimited

    Tom HT (@tomh-t) where should that be placed? Ben Ilegbodu (@benmvp) posted a similar fix a few years ago, but I can't either yours or his working. Typescript vis VS Code shows a missing property assignment near the end of the block. I tried at the top of the describe block, as well as inside the beforeEach block.

    Image

  15. tomh-t commented on Jun 30, 2025

    @tomh-t

    Andy Matthews (@commadelimited) - looks like a small syntax error you need to fix. I wouldn't trust a copy & paste in a github comment character for character - I have to edit it in here to avoid sharing irrelevant code and writing code in a comment is susceptible to syntax errors (missing curly)

    But anyway - we're using this in a helper

    export const withLocationMock = () => {
      const oldWindowLocation = window.location;
    
      beforeEach(() => {
        Object.defineProperty(window, 'location', {
          configurable: true,
          value: Object.defineProperties(
            {},
            {
              ...Object.getOwnPropertyDescriptors(oldWindowLocation),
              assign: {
                configurable: true,
                value: jest.fn(),
              },
              reload: {
                configurable: true,
                value: jest.fn(),
              },
              host: {
                configurable: true,
                writable: true,
                value: 'initial-host-for-tests',
              },
              protocol: {
                configurable: true,
                writable: true,
                value: 'http:',
              },
            },
          ),
        });
      });
    
      afterEach(() => {
        Object.defineProperty(window, 'location', {
          configurable: true,
          value: oldWindowLocation,
        });
      });
    };

    And then in any test

    describe('some test', () => {
      withLocationMock();
  16. commadelimited commented on Jun 30, 2025

    @commadelimited

    Thanks my friend, I'll give that a shot...appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Needs InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions