Annotations and Assertions 12 exercises
solution

Simplifying TypeScript Annotations

As a reminder, here's the starting point of the isProblemOrSolution function:

const isProblemOrSolution = (filename: string): boolean => {
  const splitFilename: string[] = filename.split(".");

  const finalIndex: number = splitFilename.length - 1;

  const extension: string | undefined =
Loading solution

Transcript

00:00 Okay, let's give this a go. The first one we can remove, in fact, I think we don't need to have this split file name annotation because this split file name annotation, as we can see, we basically say, okay, file name at the start is a string, and then when we split it, the split function returns an array of strings here.

00:18 So this one just doesn't make any difference. So with it, it's sort of inferred as an array of strings, and without it, it's inferred as an array of strings. The same is true for this final index here because split file name dot length minus one, that's going to be just a number, whether we annotate it or not. Extension string are undefined.

00:37 We're doing an indexed access in here, and so because of that, it means that we might index into the right place or not, and so it might be string or it might be undefined. And again, this Boolean can go away because everything that returns from a kind of like logical comparator here,

00:57 which is the triple equals, that's gonna be a Boolean and the same for isSolution. Then the final one that we might be able to remove is this Boolean here, is the Boolean on the return type. Can we remove that while still retaining the same? Yes, we can because TypeScript understands that we're comparing a Boolean to a Boolean here,

01:16 so we're saying isProblem or isSolution. So this means that the thing that comes back from the function is going to be a Boolean. The final one, the final TypeScript annotation here is this function parameter here. And you might think, oh, really? Why can't we add that? Because we know that filename.split,

01:35 like if we call .split on something, it's probably going to be a string. So why isn't filename inferred a string there? Well, this is one place where TypeScript demands that you add an annotation. So in this case, TypeScript doesn't understand really or doesn't, I think it doesn't want to make the assumption that really, like,

01:54 because this could be just any old object that has split as a property on it. And then TypeScript doesn't really know what split might return. So if TypeScript does too much inference here, then it could end up being unsafe. And certainly TypeScript would have to do a lot more work.

02:12 So by annotating the function parameter here, you're basically saying to TypeScript, okay, you can do just inference based on a minimal piece of information that I give you. So that's why function parameters always pretty much need to be annotated. If we go down to the second example here,

02:30 then this array of users here, we've got an array of users, which is basically just being inferred as an array of objects with a name property. We can remove this annotation. So if we check that, okay, that's what it's being inferred as already. Then we remove that annotation and bam, it's being inferred as the same thing.

02:49 Of course, if we were to put this as an as const, it would be inferred differently, but this is basically all we need for this at the moment. And then users with IDs here, we've got ID number name string here. And if we remove this annotation, then we can see that it just behaves exactly the same, ID name string, because it understands what's being returned

03:09 from the map function, the .map function. And inside here too, there's an annotation. We've got users.map, user name string. We can remove this annotation. So there, and now user is still being inferred as name string. Index doesn't need to be inferred either. And we get to just remove a bunch of code

03:26 and this just looks like plain JavaScript now. And the same annotation still applies to users with ID. If we removed the ID there, for instance, now this would be a different behavior. And so we wouldn't have ID inside that inferred array anymore. So this whole setup, as you can see,

03:43 we've removed just tons and tons and tons of annotations. And we're left with something that just looks like JavaScript code, really. So take this lesson with you. You don't need to annotate that much in TypeScript, but when you do annotate, you want those annotations to be doing something. Because all of these annotations that we had before,

04:02 if we look back at the problem, they're really just describing what TypeScript is inferring anyway. So make sure that when you annotate, you annotate with intention. And that means annotating function parameters and not annotating things that don't require it.