remix-run / history

Manage session history with JavaScript
MIT License
8.29k stars 961 forks source link

Location type should have template for unknown for state #930

Open elijahrdorman opened 2 years ago

elijahrdorman commented 2 years ago

Any typesafe use of Location requires casting the unknown type into something else (casting is usually a code smell). My primary use for this is in React Router with the useLocation hook when accessing the state property.

It seems like a template variable would fix this up. Perhaps something like this would work.

export interface Location<T = unknown> extends Path {
    state: T;
    key: Key;
}
luotongzhou commented 2 years ago

I thinks it's a good idea

mkerkstra commented 2 years ago

Passing a generic for location.state used to give a generic (see history/api-reference) Screen Shot 2022-02-10 at 11 19 34 AM

The generic was removed in this commit.

location.state is like location.search, there's no way for a type system to do its job with a value that isn't controlled by the code. Similarly, the DOM type for URLSearchParams is not generic. And the built-in types for window.history.state is any without a generic. The only way to type check location.state or location.search or really any value on location.* is to do it at runtime and then get type hints from using type guards.

XavierDefontaine commented 1 year ago

@elijahrdorman Seconded. This temporarily fixed the problem for me with the Class hoc:

export interface ILocationState<Type> extends Omit<Location, 'state'> { state: any | Type };

export interface IWithRouter<Type = any> {  //TODO: make this a mandatory Type so it's easier to keep track of State between components.
  router: {
    location: ILocationState<Type>;
    navigate: NavigateFunction;
    params: Readonly<Params<string>>;
  }
}
};

Usage: