In Typescript, what is type PoorMansUnknown = {} | null | undefined and how would you deal with it?
Asked Answered
N

1

8

Can someone please help me understand the meaning of the following type declaration...

type PoorMansUnknown = {} | null | undefined;

It is a TypeScript declaration and I have come across it in the context of the history package.

I have searched around to try and understand it myself but, given I'm quite new to TypeScript, it has me a bit stumped. I have found the following information on it:

My assumption is at this point, the author of this declaration intended it to be a way of saying... at runtime, this thing could be either an empty object or null or undefined.

For more context, the way this type turned up on my radar is within the package react-router-dom (typedef here). Specifically, I went to use the component Redirect and it's property to: object since I wanted to be able to tell the component I had routed to the location I was at previously

e.g.

<Redirect to={{
  pathname: "/login",
  state: { from: "current-path }
 }} />

The doco for react router says...

The state object can be accessed via this.props.location.state in the redirected-to component. This new referrer key (which is not a special name) would then be accessed via this.props.location.state.referrer in the Login component pointed to by the pathname '/login'

So, I go to access props.location.state as such...

const { from }  = location.state || { from: { path: '/' } }

and I get an error

Property 'from' does not exist on type '{} | { from: { path: string; }; }'.

I trace this through the typedef chain, starting with LocationState and make my way down to the declaration for PoorMansUnknown

This is where my confusion starts kicking in. What is this type? How should I deal with it?

I found something related to it here, which says to cast it as any. So I did that, as such:

const { from }  = location.state as any || { from: { path: '/' } }

But I'm not sure about that. It does work, but is it just a workaround?


Long story short, have you ever come across something like this before?

type PoorMansUnknown = {} | null | undefined;

and if so, what do you think it means and how would you work with components that make use of it?

Newport answered 20/3, 2020 at 5:14 Comment(7)
maybe a dumb question, but why do you need to know where you came from? I usually avoid putting stuff in history state in regards to application behavior because that can be overwritten and potentially add errors / variability to the codes behaviorOrsa
are you asking what is Typescript union type typescriptlang.org/docs/handbook/… ?Spoilfive
@JohnRuddell, thanks for replying. The idea there was to be able to return a user back to the originally requested protected route after they had successfully logged in. i.e. if not logged in and navigate to a protected route, route them to the login page and, if they successfully login, redirect them back to where they originally asked for. Upon thinking about your reply, perhaps it's unnecessary.Newport
@Crocsx, thanks also for clarifying. I'm looking to understand what the creator of PoorMansUnknown might have intended. I was a bit confused why the LocationState eventually ended up down at PoorMansUnknown when you traced it through. It has stumped me a bit.Newport
You should just use a standard redirect format here. Aka www.my.domain.here/login?r=<redirect url here>. you can use redirect or r as the param, whatever makes more sense to you. It's better to use a querystring parameter with the redirect url, rather than in the history state. because the state can be cleared or reset from another package, 3rd party library, chrome extension... etc. It'll break your application. Rather you should just rely on the given url. If you want help fixing the typings we'd need to see more code around the usage, how the types are set on your component... etc.Orsa
Thanks @JohnRuddell, that's good advice, much appreciated 👍Newport
Just re-visiting this question to share some additional information. With the passing of time and a bit more experience I'm understanding this much better. Definitions like this are simply a shorthand way of defining something as having multiple possible types without having to type out the whole thing each time. Here is another similar one that I've seen recently... type Maybe<T> = T | null <<<--- I think its a neat little style.Newport
P
11

PoorMansUnknown is supposed to be equivalent to the unknown type. We use PoorMansUnknown when unknown is not available, i.e. because we have to support an older version of TypeScript that does not support the unknown type.

"Unknown" is the default type of history state. You can customise it:

type Props = RouteComponentProps<{}, StaticContext, { from: { pathname: string; }; }>;

const MyComponent: FC<Props> = ({ location }) => {
  const { from }  = location.state || { from: { pathname: '/' } }
  // …
}

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/ee646ad6a1cd47f71f099c0a03686d8f3547f3b0/types/react-router/test/examples-from-react-router-website/Auth.tsx#L72-L102

Phonic answered 21/3, 2020 at 13:33 Comment(4)
Thanks Oliver, that definitely helps explain it for me. Much appreciated. That would be this unknown type, correct?Newport
Yes that's the onePhonic
how could it be modified to make it work for useLocation hook ? @OliverJosephAshHola
@Hola just pass whatever type definition you have to useLocation. Example: useLocation<{ from: { pathname: string } }>().Briarwood

© 2022 - 2024 — McMap. All rights reserved.