TypeScript Type Challenge Pick Walkthrough

| 2 min read

The goal of the challenge is to reimplement the built in Pick generic type that exists in TypeScript, without using Pick itself.

The challenge: https://github.com/type-challenges/type-challenges/blob/main/questions/00004-easy-pick/README.md

Watch the video

An example case

Given the following TypeScript:

interface Todo {
title: string
description: string
completed: boolean
}

type TodoPreview = MyPick<Todo, 'title' | 'completed'>

Given the interface Todo above, we need to create a generic type MyPick that takes the type (Todo) and a union of the properties that we wish to pick from it. The base code would look something like:

type MyPick<T, K> = ?

Approach

If we were creating a function in JavaScript to do a similar job, we'd want to loop over everything in K and construct a new object with each entry from K as a key. We'd set that key value equal to T[i] to reference the original value of T for that key.

We can do something similar in TypeScript types with mapped types. The syntax for a mapped type is:

type MyPick<T, K> = {
[Property in K]: T[Property]
}

Where property is bound to each entry in our K type union.

The solution

The above creates a new type from the existing type T, with only the keys from K in it, as it loops over the entries in K only. However it doesn't work as TypeScript has no guarantee that the type K is a union type. For example:

MyPick<Todo, number>

Would not through any TypeScript errors as both T and K are types, Todo and number respectively. Clearly we need to restrict the type of K to be a union type of keys that exist on T.

type MyPick<T, K extends keyof T> = {
[Property in K]: T[Property]
}

This means that even if K is a union, if it includes properties that do not exist on T, it will result in a compiler error.