question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Consider using `unknown` instead of `any` for `location.state`

See original GitHub issue

Since v5.0.2, location.state now has type any. https://github.com/remix-run/history/commit/04a01ad4500720e26ebbf3d9cfefbd6c1a86be84

I appreciate why generics were not useful here, however I think it would be better to unknown instead of any.

any completely disables type checking, meaning we can make mistakes such as:

// no compile-time type error (❌) but throws at runtime
location.state.i.do.not.exist;

On the other hand, unknown will error in this case:

// compile-time type error ✅
location.state.i.do.not.exist;

We are forced to narrow and refine the unknown type using type guards:

interface ModalLocation extends Location {
  state: { foo: string }
}
 
const isObject = (candidate: unknown): candidate is object =>
    typeof candidate === 'object' && candidate !== null;

type DictionaryLike = { [index: string]: unknown; };
const isDictionaryLike = (
    candidate: unknown,
): candidate is DictionaryLike => isObject(candidate);

const isModalLocation = (location: Location): location is ModalLocation => {
    return (
        isDictionaryLike(location.state) &&
        typeof location.state.foo === "string"
    );
};

// compile-time type error ✅
location.state.foo;

if (isModalLocation(location)) {
  // no compile-time type error ✅
  location.state.foo;
}

The commit message mentions that one reason to use any is because that’s how TypeScript defines window.history.state:

the built-in types for window.history.state is any

However, I’m not sure this is a good precedent. Like many other built-in types, the definition of window.history.state was written before TypeScript had the unknown type. TypeScript is gradually moving towards unknown. For example, the type of an error in a catch block used to be any but it is now unknown in strict mode.

If a user doesn’t care about validation and type safety, they can use a type assertion as an escape hatch.

// no compile-time type error ✅
(location.state as any).i.do.not.exist;

This way, type safety is the default, but the user can easily opt-out.

On the other hand, if we continue to use any as the type for location.state then users who do care about type safety might not realise that an any type is slipping into their code.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:4
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
samhhcommented, Nov 2, 2021

I think you can make the case that any is a massive footgun and therefore its removal is a bugfix, which is semver-compliant.

2reactions
timdorrcommented, Nov 2, 2021

OK, 5.1.0 is out. Phew!

I would also vote this as a good idea, but it’s probably going to need to be a major version. Any TS user is going to immediately get errors, unless they are already diligent about type guards for state. I don’t think most are, which is understandable, so that would be disruptive despite being the right thing to do.

Read more comments on GitHub >

github_iconTop Results From Across the Web

React useHistory, Object is of type 'unknown' - Stack Overflow
I was trying to do something similar to get the previous URL from which the current component has been navigated to. Here is...
Read more >
Typescript: why you should use unknown instead of any
The unknown type, when assigned to a variable, means that a variable type is not known. And typescript doesn't allow you to use...
Read more >
Understand & manage your location when you search on ...
When you search on Google, like with Maps, Search or Google Assistant, your current location is used to give you more helpful results....
Read more >
The unknown type in TypeScript - Medium
We can force the compiler to trust that an unknown varible has a specific type: const bar: string = foo as string ....
Read more >
Tomb of the Unknown Soldier - Arlington National Cemetery
The Tomb of the Unknown Soldier, with carved figures representing Peace, ... would be transported to anywhere in the United States at no...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found