Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I type properly "JSON.parse()" in ts?

Tags:

typescript

JSON.parse(aString) is typed as any by default. However, any also includes functions, dates, regexes and other classes which cannot be parsed.

Now I suspect we can be more accurate if we make use of the conditional types introduced in ts 2.8 and inference of the 3.0 but I am a little bit stuck on how to do it. Perhaps there is some existing basic type I am missing, could anyone advise me?

const good: BasicType = JSON.parse(aString); // what I need
const bad: any = JSON.parse(aString); // what I do have now

[EDIT] It is now possible to have recursive types and therefore to type JSON objects properly, typefest being one example of implementation.

like image 336
Flavien Volken Avatar asked Nov 15 '25 16:11

Flavien Volken


1 Answers

As far as I know there is no built-in type name in the TypeScript standard library that corresponds to types that can be serialized-to/deserialized-from JSON. GitHub issue microsoft/TypeScript#1897 suggests adding one, although it doesn't look like there has been much movement there.

Luckily it's easy enough for your to create your own type for this, such as this one suggested in a comment in the aforementioned GitHub issue:

type AnyJson =  boolean | number | string | null | JsonArray | JsonMap;
interface JsonMap {  [key: string]: AnyJson; }
interface JsonArray extends Array<AnyJson> {}

And then you can use declaration merging to overload the parse() method to return AnyJson instead of any:

interface JSON {
  parse(
    text: string,
    reviver?: ((this: any, key: string, value: any) => any) | undefined
  ): AnyJson;
}

(note that if your code is in a module you will need to use global augmentation to get the above to work)

And let's see if it works:

const x = JSON.parse('{"id": 123}'); // x is AnyJson

Since x is known to be AnyJson and not any, you could now do type guards on it and the compiler should be more aware of its possible structures:

if (x !== null && typeof x === "object" && !Array.isArray(x)) {
  // x narrowed to JsonMap
  Object.keys(x).forEach(k => {
    const y = x[k];
    // y is also AnyJson
    console.log("key=" + k + ", value=" + String(y));
  });
  // key=id, value=123
}

So that all works. That being said, the type AnyJson isn't a whole lot more specific than any, practically speaking. I suppose you gain the benefit of excluding functions/methods from possible values/elements/properties, which isn't nothing... it just might not be useful enough to warrant modifying the TypeScript standard library. As said in this comment, the AnyJson type is "within spitting distance of any".

Link to code

like image 51
jcalz Avatar answered Nov 18 '25 10:11

jcalz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!