I have created a new custom command for myself
This one, as the name states, is going to edit a board.
I haven't passed anything into this command, and it's already saying that I should be passing at least 1 argument, but I got 0.
Let's go ahead and take a look into our custom command.
Right now, it looks very similar to our
addBoard command that we talked about in the previous chapter, but I want to make it type safe, and actually use types from my
Board interface that I already have in my application.
Since I want the
body to be of type
Board, I can just go ahead and pass it.
Then when I take a look into my
spec file, I can start typing an object, and I have all of these auto suggestions of what I want to type, but my API actually does not allow me to use everything.
For example, I cannot change the
user or I cannot change the
created date when that board was created.
This means that I cannot use my interface as it is currently defined.
What can I do? There are multiple options that I can choose.
The bare minimum I can do is to use something that's called utility types.
One of those utility types is something called
This enables us to define what kind of keys we want to type, and what kind of parameters those keys should have.
If I type something like
<string, any>, this will mean that inside
editBoard, I can just pass any object.
Anything that will have a parameter and a value will be good enough.
I can even pass an empty object, and my
editBoard function would still not complain.
Now, this is less than ideal.
We need to be more specific, but there are some cases where we don't really care what kind of object we pass, as long as it's an object.
Let's take a look into another utility type, and that's called
This enables us to pick any attributes that we want from an existing interface.
I'm going to use
I paste it into my custom command, and when I now save my file and go to my
spec file, you can see that my TypeScript compiler is complaining.
I simply cannot pass an empty object.
What I need to do is pass an
If I do that, the errors disappear.
Now, if I take a look into my custom command and what it actually does, I can see that it will call a
PATCH request to
/api/boards/ with a certain id, and then we are going to be passing the
body that will contain all of the changes that we want to make.
For example, if we take a look into this interface, we might want to change the
name of our board, or maybe even the
If we were to pass any of this into our command - for example,
name: 'my board', the TypeScript compiler is going to complain about that.
The reason for that is that we simply picked only the
If we want to add multiple keys that we want to include in this object, we can add a pipe character
| and then type the other attribute that we want to add.
As you can see, the errors have already disappeared.
But also, we might want to change the
starred attributes, so let's add that one as well.
When I add this, the test is going to complain, but I'm going to add it and pass
All of these keys are type checked, so in my interface, the
starred attribute is a
boolean, which means that if I change this to a
string, I'm immediately getting an error.
I'll change it back and go back to my
Pick utility type is quite useful, but I can choose an opposite approach, and just pick all of the keys from my board interface, but leave out some of them.
To do this, I can use the utility type called Omit, and this is going to do the exact opposite.
Instead of requiring
starred, it's going to require all of the others - so that would be
If we want to keep our function working, we need to add those into our
Omit utility type - so that's going to be
created | user.
Now everything is looking good.
The only problem might be that whenever we call
editBoard, we are required to call both
name and the
starred attribute, which is something we might not always want to do.
What if we just want to change the name or just want to change the starred attribute?
In that case, we can go ahead and define our attributes explicitly.
What I'm going to do is I'm going to define an object, and I'm going to say that there will be a key of
id that's going to be a type of
Then, I'm going to say that there's also an attribute of
name with the type of
Board['name'] and an attribute
starred, which will be a type of
The TypeScript compiler is now satisfied, but it still didn't solve the problem.
If I delete my
starred attribute, the compiler is going to complain about the fact that I haven't included the
What I can do now is say that the
name attribute and the
starred attribute are optional.
To do that, I'm going to type this question mark
? to both of them.
This means that I am required to use the
id, but I don't really have to add the
name or the
If I were to decide that all of my attributes were optional, I could add the question mark to all of them, but also I could use another utility type that's called
With this one, I can say that the argument of this
editBoard function can be a
This means that if I start typing, I get these suggestions for different attributes, but none of them are required.
If I take a look into what
editBoard actually does, it seems that it is always going to require
We basically always need to pass it if we want our API call to work.
What I can do is that I can actually combine these utility types.
I'm going to keep the
Partial<Board>, but then I'm going to add to this type another type that's called
In this one, I need to define which types are required, but I'm actually going to pass a
Pick utility type, and from my
Board, I'm going to pick the
editBoard, I am required to use the
id, which will be a
But I also have the optional keys, so if I want to pass the
name, I can pass it, or I can pass the
starred say it's, for example,
One more thing that is kind of annoying about this is that I have two different types in my
editBoard type definition and in the function itself.
Usually, I need to type everything twice to include that both in my custom command, and also in the type definition.
There is a different way of how I can do this.
Basically, I'm going to copy the whole function and define it as a function of its own.
I'm going to call it
editBoard, and paste the function in here.
Now, the thing that I'm going to do here is that I'll use this function as an argument into my
editBoard() function will actually be a
Everything still works as before.
If I delete my
id, it's going to complain.
I get the auto-completion, but in my custom command, I just need to write everything once.