1

How do I resolve this Typescript issue?

const userData: {
      email: string;
      id: string;
      _token: string;
      _tokenExpirationDate: string;
    } = JSON.parse(localStorage.getItem('userData'));

enter image description here

Message from Console

Error: src/app/auth/authservice.ts:56:20 - error TS2345: Argument of type 'string | null' is not assignable to parameter of type 'string'.
Type 'null' is not assignable to type 'string'.

I added the // @ts-ignore but WebStorm is complaining that there is an error.

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
software is fun
  • 7,286
  • 18
  • 71
  • 129
  • 2
    And what do you expect to happen if the storage is empty? – jcalz Sep 20 '22 at 21:20
  • Well if it's empty wouldn't userData be null then I return from the login method and the user is not authenticated – software is fun Sep 20 '22 at 21:21
  • 1
    Yes, but TypeScript is of the opinion that `JSON.parse()` should be passed a string, not a `null`. If you want to get something a little more type safe (but `JSON.parse()` returns `any` so it's never going to be safe by itself) then you should refactor so that you only pass a string, like [this](https://tsplay.dev/w23VYN). If you just want to silence the error then you can do [this](https://tsplay.dev/NBRLpN) and note that I corrected the annotated type to include `null`. What do you want to see here? – jcalz Sep 20 '22 at 21:28
  • @jcalz your idea is brilliant. Please post it so I can credit you with the solution – software is fun Sep 20 '22 at 21:36
  • Stack Snippets are for HTML/CSS/JavaScript code. You added TypeScript in the HTML box. Click "Run code snippet" in that case does not illustrate anything interesting. Please use Stack Snippets for runnable code only. Otherwise, just use the regular code button, that looks like `{}`, after highlighting the code. – Heretic Monkey Sep 20 '22 at 21:48

2 Answers2

2

Let's ignore the fact that JSON.parse()'s call signature declaration returns the unsafe any type. I'll just assume that if JSON.parse() returns anything other than null, it will be a value of your expected object type.


It's possible that localStorage.getItem("userData") might be null, and TypeScript thinks that it's a mistake to call JSON.parse(null), since generally the point of JSON.parse() is to parse strings that encode JSON values. Parsing null is therefore a type error, even though JavaScript will not have a runtime error. See What does "all legal JavaScript is legal TypeScript" mean? for more information.

If you're trying to fix it to be more type safe, you should make sure only to pass a string. Perhaps like this:

const retrievedValue = localStorage.getItem('userData');
const userData = retrievedValue ? JSON.parse(retrievedValue) as {
  email: string;
  id: string;
  _token: string;
  _tokenExpirationDate: string;
} : null;

This compiles without error, and now userData has the type {email: string ... } | null, where the union with the null type reflects that userData might either be a valid object or null.


If you want to be expedient but a little less safe, you could just lie to the compiler about the local storage result and assert that it is not null using the non-null assertion operator (!):

const userData: {
  email: string;
  id: string;
  _token: string;
  _tokenExpirationDate: string;
} | null = JSON.parse(localStorage.getItem('userData')!);

Note that I added the | null manually there, since the compiler can't see that possibility anymore.

Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360
1

The easiest way to solve your problem IMO is to just do this:

const userData: {
  email: string;
  id: string;
  _token: string;
  _tokenExpirationDate: string;
} = JSON.parse(localStorage.getItem('userData') ?? "null");

Note the nullish coalascing operator (??) requires typescript 3.7


Now in the case that you want your type to reflect the possibility of being null (in case localStorage does not contain 'userData'), then you probably want to use a union type with null:

const userData: {
  email: string;
  id: string;
  _token: string;
  _tokenExpirationDate: string;
} | null = JSON.parse(localStorage.getItem('userData') ?? "null");

At this point, you might as well extract that userData type to an interface:

interface UserData {
  email: string;
  id: string;
  _token: string;
  _tokenExpirationDate: string;
}

const userData: UserData | null = JSON.parse(localStorage.getItem('userData') ?? "null");
smac89
  • 39,374
  • 15
  • 132
  • 179