TypeScript first appeared in October 2012, and it was one of those tools that at first I didn’t like. Mapped types first version arrived in TypeScript 2.1, which was released nearly four years after the first release. Looking at Google Trends, we can see how interest started to grow in 2015 (Visual Studio Code release date) and then really took off in 2016. Mapped types and Angular 2 were released in 2016, which should account for that growth. The Basics What are mapped types anyway? They are just a way to avoid defining interfaces over and over. You can base the type you need on another type or another interface and save yourself a lot of manual work. You can use the TypeScript Playground app below to run all the snippets included in this article. It’s definitely a playful tool where you can practice and improve your TypeScript skills. Let’s start seeing some TypeScript in action. Point { x: ; y: ; } interface number number If you want to extend this interface, you can do the following. Point3D Point { z: ; } interface extends number But you can use mapped types as well: Point3D = Point & { z: }; type number Cool, isn’t it? Although in this scenario we are not really showing anything that can’t be done with interfaces, are we? We are just getting started, though. That being said, it’s preferable to use interfaces in this particular scenario. This is just a showcase so you can see its versatility. You can easily combine two types, as follows: A { a: ; } B { b: ; } AB = A & B; interface string interface string type Here is where it really starts to shine. We can see how easy to do and readable this becomes. Before diving a bit deeper, let’s look at the primitive introduced in version 2.1. It looks scary at first, but it’s super intuitive: It will just expose the keys of any given interface/type through a union. Those are known as Typescript Union Types. keyof Book { author: ; numPages: ; price: ; } BookKeys = keyof Book; interface string number number type // 'author' | 'numPages' | 'price'; Built-In Utility Types TypeScript does ship with a lot of utility types, so you don’t have to rewrite those in each project. Let’s look at some of the most common: Book { author: | ; numPages: ; price: ; } Article = Omit<Book, >; BookModel = Readonly<Book>; book: BookModel = { author: , numPages: , price: }; book.author = interface string null number number // Article is a Book without a Page type 'numPages' // When it's using functional programming to use immutable objects type const 'Jose Granja' 2 0 'Robert Smith' // this will raise an error since all the properties are readonly — those are the most common. Omit, Partial, Readonly, Exclude, Extract, NonNullable, ReturnType You can check out all of them in more detail . here You are not limited to using only one at a time but can combine them as much as you need, and this is where the fun really starts. AuthoredBook = Omit<Book, > & NonNullable<Pick<Book, >>; type 'author' 'author' // this result in a type with a "author: string" type. Author is not nullable anymore Build Your Own Utility Types Let’s look first at how TypeScript does its utilities. We’ve used Omit, which was introduced in TypeScript 3.5. It’s a combination of two other utilities. Before starting, let’s cover how to access interface/type properties. Omit<T, K keyof T> = Pick<T, Exclude<keyof T, K>>; Exclude<T, U> = T U ? never : T; Pick<T, K keyof T> = { [P K]: T[P]; }; // definition of Omit type extends //definition of Exclude type extends //definition of Pick type extends in Pretty easy when you break it down into small units, isn’t it? does play a big role here since it will be making sure that the properties we wanted to omit to belong to the type/interface we want to be omitted. keyof Let’s create our first custom utility. Let’s say we want to define an object where all key values are from a particular type. GenericString = { [key: ]: } type string string Pretty easy, right? Let’s use Generics and make the string type configurable. GenericObject<T> = { [key: ]: T } type string Let’s make that key type configurable: GenericKeyObject<K keyof , T> = { [P K]?: T; }; type extends any in Let’s see that in action. BooleanList = GenericKeyObject< , >; countriesVisited: BooleanList = { France: , Italy: }; VisitableCountries = | | ; CountriesChecklist = GenericKeyObject<VisitableCountries, >; countriesVisitedCurrentYear: CountriesChecklist = { France: , Hungary: }; countriesVisitedLastYear: CountriesChecklist = { France: , Belgium: }; type string boolean const true true // let's take it a step further type 'Hungary' 'France' 'Germany' type boolean const true true const true true // this fails at compile time since Belgium is no in the list of visitable countries. What works for me when building complex mapped types is starting simple and then incrementally adding complexity. You might find that starting too complex is frustrating. Level-Up Your Utility Types and Usage It’s easy to build a very simple and trivial example with mapped types. Let’s now dig a bit deeper. Let’s check out the never primitive first, though. It was introduced in version 2.0. It indicates that the value will never occur. HttpResponse<T, V> { data: T; included: V; } StringHttpResponse = HttpResponse< , never>; interface // let's use never type string // What you are here doing is banning StringHttpResponse consumers from using the included property as even in other usages of HttpResponse it might be populated it is not in this particular response. You are communicating that this property must be ignored Let’s now check out the primitive added on version 2.8. infer As you know, TypeScript relies heavily on type inference. You can refresh your memory . here What this primitive does is it empowers your mapped types with inference. Now you can extract and infer a type inside a conditional type. infer What is a conditional type? It’s just an expression for selecting one of two possible types based on a condition expressed as a type relationship. T U ? X : Y extends Let’s put all that into play. ResponseData { data: []; hasMoreItems: ; } getData = (): <ResponseData> => { data = { data: [ , , , ], hasMoreItems: } .resolve(data); } interface string boolean const Promise const 'one' 'two' 'three' 'four' false return Promise What if you wanted to unpack the type from the returned by ? Simple: Create your own utility using . Promise getData infer Unpacked<T> = T (infer U)[] ? U : T (...args: []) => infer U ? U : T <infer U> ? U : T; PromiseResult = Unpacked<ReturnType< getData>>; FunctionResult = Unpacked< >; type extends extends any extends Promise type typeof // PromiseResult = ResponseData type => () string // FunctionResult = string What’s happening there? You are just inferring the type if T extends a or a ; otherwise, you are just returning the given type . Promise Function T Let’s do one last one as a bonus. Let’s do an interface that will return if the object is empty and if the type has some properties. Yes, we can do crazy stuff like that in TypeScript. true false Empty<T {}> = {} Required<T> ? : ; isEmpty = Empty<{}>; isNotEmpty = Empty<{ name: }>; type extends extends true false type // true type string // false Why is that useful? You can combine the mapped type Empty with any conditional infer/mapped type to create more custom types. So we just created a tool that will help us create more mapped types. HttpDataReponse<T> = Empty<T> ? never : T; ClassReponse = HttpDataReponse<{}>; BookReponse = HttpDataReponse<{ author: }>; type extends true type // result be never type string // result be { author: string } Pretty crazy what you can achieve, isn’t it? The limit is your imagination. TypeScript Tuples One of the last additions and improvements was Tuples. Tuples are arrays where the number of elements is fixed. Their type is known and they are not necessarily the same type. arrayOptions: [ , , ]; arrayOptions = [ , , ]; arrayOptions = [ , , ]; { .log(data); } printConfig(arrayOptions[ ]); let string boolean boolean 'config' true true // works true 'config' true // does not work ( ) function printConfig data: string console 0 is very useful. There’s one scenario where it’s very common to find it nowadays: React Hooks. The Hooks implementation normally returns an array with the result plus functions, and having that array typed is indeed very handy. Tuple Wrap Up And just like that, we are done. We started with some basic mapping and ended up with some Typescript advanced types. Mapped types are by far the most fun feature in TypeScript. They serve as a good showcase of how powerful and dynamic TypeScript can be. If you were hesitant to try TypeScript, I hope my article gave you that very last push you needed. Even if you are not planning to use it now, it is wise to know what’s out there in case it comes in handy in the near future. For those heavy TypeScript users, I hope you can agree on how much fun mapped types are once you get the hang of them. More TypeScript content will be coming in the future — Cheers! Also published at https://medium.com/better-programming/mastering-typescripts-mapped-types-5fa5700385eb