Consider using `unknown` instead of `any` for `location.state`
See original GitHub issueSince 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
isany
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:
- Created 2 years ago
- Reactions:4
- Comments:7 (5 by maintainers)
I think you can make the case that
any
is a massive footgun and therefore its removal is a bugfix, which is semver-compliant.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.