Massaging types in typescript (part 1)
Published: 2021-01-12 07:25:43

TLDR;

In TypeScript, we can retrieve type information in many different ways. In *type context* we can use:

- **typeof** - refer to the type of a value or a property: `typeof "some string"` is `string`; `typeof 1` is `number` etc.
- **keyof** - given an object, reduces it to an union of it's keys: `type keys = keyof { name: "Bobi", age: 29 }` resolves to the following type: `type keys = "name" | "age"`
- **indexed access** - given `type Person = { name: "Bobi" }` and `type PersonName = Person["name"]`, the `PersonName` resolves to `string`

---

TypeScript is great when it comes to types reusability. Once defined, a type can be "massaged" in order to suit different business logic needs.

Here's one simple example.

```typescript
type Person = { name: string, age: number };

const person: Person = { name: "Bobi", age: 29 };

const updateName = (p: Person, name: string) => {
    p.name = name;
}

updateName(person, "John"); // { name: "John", age: 29 }
```

The `updateName` function accepts a person (`Person`) and a name, which is in the form of a `string`.

It's all great, but imagine we get new requirements, which say a user can have an empty (`null`) name.

We do:

```typescript
type Person = { name: string | null, age: number };
```

Now we have to also update the type of the `name` argument in `updateName` like this:

```typescript
const updateName = (person: Person, name: string | null) => {
    person.name = name;
}
```

This move can be skipped if we were using **indexed types access** for the `name` property of the `Person` type. Let me show you.

```typescript
const updateName = (person: Person, name: Person["name"]) => {
    person.name = name;
}
```

Having this type definition, typescript is smart enough to resolve the type for the `name` argument by itself, using the type of `Person.name`. Which, of course, is `string | null` after the update with our new imaginary requirements.

That's the power of **indexed types access**

## The `typeof`

Another handy keyword in TypeScript is `typeof`.

```typescript
const names = ["John", "Mary"];

const emojify = (name: string) => {
    return `${name} 🤘`;
}

const process = (items: typeof names, processor: typeof emojify) => {
    return items.map(processor);
}

console.log(process(names, emojify)) // ["John 🤘", "Mary 🤘"]
```

Pay attention to the `process` function. It's `items` argument is *anything* that has the same *type* as names, which happens to be `string[]`. And the `processor` argument is *anything* that has the same *type* as `emojify`, which in the example is `(string) => string`.

We use `type x = typeof y` when we want tell the compiler: "Hey, compiler, whatever the type of that variable (`y`) is, use it as a type for this (`x`) variable".

## The `keyof`

The last tool we're going to cover in the first part is `keyof`. 

It's quite self explanatory. Let me show you.

```typescript
type Subscriber = { id: number, email: string, topic: number };

type SubscriberProp = keyof subscriber; // "number" | email" | "topic"
```

Our `SubscriptionDetail` gets resolved to the keys of the `Subscriber` type. So we get `type SubscriberDetail = "email" | "topic"`.

It's handy when we need something in the following fashion:

```typescript
const getProp = (subscriber: Subscriber, prop: SubscriberProp) => {
    return subscriber[prop]
}
```

Part two is in the oven and is coming soon.

<span>Photo by <a href="https://unsplash.com/@oneshotespresso?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Hans Vivek</a> on <a href="https://unsplash.com/s/photos/massage?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>