Check out a free preview of the full TypeScript Fundamentals, v3 course:
The "JSON Types Exercise" Lesson is part of the full, TypeScript Fundamentals, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Mike walks through an exercise of defining correct types to describe given JSON values and suppress the thrown type check errors.

Get Unlimited Access Now

Transcript from the "JSON Types Exercise" Lesson

[00:00:00]
>> So we're going to begin a collaborative exercise. And what I would like to see us do here is, write some types using interfaces or type aliases, and then what makes sense to you. But we want these types to describe any valid JSON value. And I have for your convenience brought a portion of the JSON specification, and so we wanna make types that describe what we're seeing on the screen here.

[00:00:36] So a value can be an object, an array, a number, a string, false, true or null. So you can I mean, I've taken these words right out of the spec document, but we can take false and true that's Boolean, right? So it's numbers, strings, Booleans, No, and then arrays and objects that include those things, but we couldn't have a function for example, functions don't belong in JSON.

[00:01:04] Make sense to everybody. If you've worked with JSON before, I'm almost certain. So, this is your starting point code, and what you have here are some positive test cases, all these should pass the TypeScript should be happy with these and then negative test cases. So these should fail and the errors you're seeing here are because they're not currently failing.

[00:01:29] This Ts expect error this special comment, this tells the compiler that it should yell at us, if it doesn't find an error here. So the fact that they're not failing, because right now we're saying, look these things are anything right? So it's happy with a function when it shouldn't be, that's why we call these negative test cases.

[00:01:53] We got to suppress the errors we're seeing here by making it so that this does not type check. So how are you going to attempt this exercise, and we can work on it as a class together for sure. But I'd like you to have a local copy of this and play with it so that you can sort of poke around and help us get to an answer.

[00:02:20] Just hit this try button and we can see the same errors right here in the TypeScript Playground. So, basically, your job Is to make it so that these three types become the right thing that describes JSON. So here's how I want you to think about this, and I'll go one step further and I'll say it's important for this task that we're looking at right now.

[00:02:51] So, the way TypeScript parses this file, it's a Java's functions, it'll go down and it'll say, JSON object noted for that. Let's keep scanning down and let's keep looking. Look, there's a JSON array, there's a type like that. All right, well, I know where to go if I need to know about that, and it keeps going down and keeps going down and it sort of finds the fact that all these things exist.

[00:03:22] And then later on, it goes back and it starts looking at the right hand side of the equals. So as a result, there's no, it's almost like there's a hoisting effect here in that as long as you're on the right hand side of the assignment operator here. You have access to any type alias that exists in scope, and that by the way is also how the recursion works, right?

[00:03:47] A lot of implementations of Recursive structures like these, they have to do with sort of scanning through and separating, declaring that something exists from how it's implemented. That's a common way to address circular referencing cuz the core problem there is, well, how can we know what it is when it's referring to things that haven't been defined yet.

[00:04:12] But if you do it in two pieces, so, you're good to go. Okay, let's get started here. So, I really like failure driven development. I like to have a bunch of errors and I can sort of SWAT at them and make them go away piece by piece. And right now, we're in this situation where we're just like way, way, way too flexible.

[00:04:37] We're letting anything pass through, that's why our positive test cases are all passing and our negative test cases are largely failing with the exception of this big int thing, which apparently is fine. Apparently that it's already failing, interesting, so cool. Let's just tighten things up a little bit more and let's start from sort of the narrowest, the most specific constrained side of things, and then add things until we get all of our cases passing.

[00:05:16] So for now, I'm gonna define a new type here. We'll call it a primitive, and let's just say for now it's a string. So JSON values, they can be strings, they can be numbers, or Boleans, or No. So here we're making good use of our union type operator, string or a number or Boolean or No, those are valid JSON values.

[00:05:54] And down here we can see our negative test cases about they are happy because without this comment, this is failing type check. So with the comment it's now happy, but we're missing our array case and our object case. And certainly the nesting aspect we won't even know until we address these two, I don't think we have much hope of addressing nested raise, object, strings, and numbers.

[00:06:27] That's sort of the most mixed of our cases. And I'm just gonna say so we have an object and an array, I'm gonna say that primitive or an object or an array. Cool, now we're back to failing, why are we back to failing? Can someone guess, when we have this code here, why did all of our negative test cases light up?

[00:06:56] So function, somehow functions happy now, anyone have a guess, in chat or on Zoom, what if I did this? Like we're back to Eddie, somehow we're back to Eddie. Yes, Joshua. And Teresa, very good, both of you. So what we've done is we've performed, we've used this union type operator with an any.

[00:07:29] And I want you to think about it kind of like multiplying by zero in that, we were fine here when we just had it as a string or a number or a Boolean or no. But as soon as we do this, it becomes an any because it's almost like zero times anything is zero.

[00:07:49] Well, whatever type you like, or any, is any sort of just eclipses all of the other type information that you have there. Very good Joshua and Teresa, so okay, well we'll have to solve that, how would I type this JSON array? Like anyone have any ideas? No peeking at the answer, I know it's on the website but don't ruin my fun.

[00:08:24] Conceptually, what is an array in JSON? What kind of values does it contain, okay. Bala's got number, number or string, Teresa, you've got another good one. So we couldn't do this, we could try to exhaustively do this but remember we've also got arrays of arrays like a JSON array of JSON arrays, that could be objects in my array.

[00:08:54] So really, I wanna just say, look, a JSON array is an array of JSON values. There are many ways to implement this, this is just the way I'm choosing to do it, so JSON oops. It's an array of JSON values, and now if I were to take this JSON object thing out, cuz that's still an any at this point, look, our array case got happy, so that's great.

[00:09:28] Even if we nested this, even if this became a string it's still happy. So really we just need to describe these objects somehow, how would we describe an object like a JSON dictionary maybe. Anyone have ideas?
>> Index signatures.
>> Index signature, very good. So that looks like this.

[00:09:59] And then we can bring it back into the fold, and there we are positive cases happy negative cases happy. So I saw some other things in the chat. I'm not gonna call any names cuz I don't wanna I like that people are collaborating, even if the answers aren't quite there, if we had this so this was one of the answers I saw in chat.

[00:10:24] What are we saying here? What's the difference between this and that. This is a tuple of length one that contains a JSON value, it's always gonna be one long, which means this case here would fail, but this case here would pass. So we wanna make sure that we use this double box, square bracket notation right double square bracket to say this is an array of arbitrary like not a tuple.

[00:11:07] But there we go, and this is definitely a recursive type because an array is expressed in terms of values and values expressed in terms of arrays. It's looping and looping all over, but it's fine because remember all the compiler does is it goes through and it says noted these things exist types of the following names.

[00:11:29] And then it sort of has a placeholder for those in memory and then on the right it says, okay, well, I have a placeholder for this, I don't know what It is yet, but okay, whatever JSON value is you'll have one here. And then it kinda goes through and sorts things out.

[00:11:47] But there you go, this would be suitable if you use the fetch API and you have a wait fetch that JSON something this, right? Maybe you could say, instead of returning any promise, maybe the value that comes out of this promise is a JSON value doesn't contain functions, only JSON.

[00:12:19] All right, if you want to look at the solution it's down here behind the spoiler warning. So if you wanna look at it later and poke at it, maybe on your own time if you like try different implementations. See if you can make this work with interfaces or a combination of the two, this is a really useful thing, everyone needs types for JSON.

[00:12:43] But maybe one day it'll be part of what TypeScript ships part of the standard stuff that comes with it.