Create Explicit and Readable Type Declarations with TypeScript mapped Type Modifiers

Rares Matei
InstructorRares Matei

Share this video with your friends

Send Tweet
Published 4 years ago
Updated 2 years ago

Using the optional “+” sign together with mapped type modifiers, we can create more explicit and readable type declarations. We can also use the “-” (minus) sign to remove optional declarations from properties.

Instructor: [00:00] If I have this interface, I can use mapped type modifiers to make all of its properties read-only. A type like this can be extremely useful for assigning as a piece of state to my Redux app, for example, because state needs to be immutable.

[00:17] I should not be able to reassign any of its properties, once the object has been created. Mapped type modifiers were a great addition to the language, as they make it easy to augment existing types and apply bulk changes to all of their properties.

[00:33] Now, if I declare two pets here, one mutable and the other read-only, and then I try some mutations on them, you'll notice that I can reassign the age just fine for the first one, but not for the second one.

[00:47] In this case, TypeScript is telling me that the age is a read-only property. That's normal, because I have marked all of its properties as read-only in here. The read-only status is not the only thing I can change in mapped type modifiers.

[01:01] I can also mark all of the properties as optional. I can make all of them strings, or I can make each of them as a union of their original type and the string. The possibilities are kind of endless. With this syntax, I can only add new things to my existing types.

[01:18] I can add a read-only flag, or I can add an optional flag. What if there's an existing optional flag and I want to remove it? What if my original type had a new property -- let's call it favorite park -- that was optional? Then I wanted this type to also remove all of the optionals from this one?

[01:36] Since TypeScript 2.8, I can now add a minus sign before the symbol I want to remove. In this case, I want to remove any optional status from all of the properties. You'll notice that the moment I added the minus sign here, TypeScript immediately started throwing errors down here.

[01:54] That's because we're suddenly missing a required property in this new object. If I just add it, the error goes away. Because we now have this flexibility with the minus sign to also remove things from our types, a plus sign has also been added to this feature. I can be more explicit about what I'm adding and what I'm removing.

[02:15] Now, it's more clear for other developers reading this type that I'm taking the original iPad interface, I'm removing all of the optional modifiers, and I'm adding the read-only flag to all of them.

Eduardo Valenzuela
Eduardo Valenzuela
~ 4 years ago

Hi, quick question, in which cases we would need to avoid modifying an interface itself and choosing modify it through a type?

Rares Matei
Rares Mateiinstructor
~ 4 years ago

Hi, Eduardo! If you can modify your interface directly then I'd recommend going for that!

Mapped type modifiers are useful if you either:

  • have an interface you can't modify directly (like one from a library)
  • have an interface that you want to continue using for some purposes, and create a slight variation of it (using mapped type modifiers) to use for other purposes

In both cases, mapped type modifiers "follow" the shape of the original interface, even if it changes/gets modified in the future, and just augment it based on the rules you specify.

James Gelok
James Gelok
~ 2 years ago

there's a typo here: readonly [K in key of IPet]: IPet[K]; should be readonly [K in keyof IPet]: IPet[K];