Use Precise types instead of String types
Tags: webdev, typescript, programming
Published: 2023-08-19 19:26:26


![TS: precise types](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54z6786qsa2d9lu73ny1.png)

## Introduction
We have all been there. Throwing `any` at function parameters and object keys because we couldn't be bothered to type it precisely. I did that for a recent project and it was a cop out.

The problem is that with `any`, we lose many of the benefits typescript offers us. The compile time error checking that can catch hard to detect bugs and also the wonderful code completion.

Today, I am gonna be discussing precise types and how they can make our code better.

## Precise Types

A bit of a background. I am currently on Item 33 of Dan Vanderkam's effective typescript.

Item 33 argues that string types are too broad and open up code to a lot of errors.

We should consider more precise alternatives that specifies the type of data we expect.

## Example 

The best way to illustrate this is with an example. 

Suppose, we have a utility function `pluck`.

The function of `pluck` is to pull out all the values for a single field in an object. The objects are members of an array.

Properly typing the `pluck` function can help in making sure that we only use existing keys
that exist in the object.

To illustrate this properly, I am going to use 4 variants of the `pluck` utility function. It would range from untyped to typed. 

Let's go:

The pluck function without any type is this:

### Version 1:
```typescript
  function pluck(records, key){
    return records.map(record => record[key])
  }
```

Here the function is untyped and while it works at runtime, it opens up a can of errors.

We aren't sure that the key we use actually exist, which means we might be trying to access a property not present. The return type is also an `any[]`. It is practically still valid `TS` code but without any benefits.

### Version 2:
```typescript
  function pluck(records: any[], key: string): any[]{
    return records.map(record => record[key])
  }
```

Here in the second version, it is slightly better but the string type is still too broad. 
Remember that this string could also not exists on the object we need to access. The `any` type is also problematic.

### Version 3:
```typescript
  function pluck<T>(records: T[], key: string){
    return records.map(record => record[key])
  }
```

We make the function a generic so that it can infer the specific type of array it is. The type checker complains.

![typeError](https://raw.githubusercontent.com/adeleke5140/portfolio-v2/main/public/images/pluckV3error.png)

We cannot use a string key on an `unknown` type.

The problem here still persists and this is because string is still broad. It is **stringly typed**. The return type is also still `any[]`

### Version 4:
```typescript
  function pluck<T>(records: T[], key: keyof T){
    return records.map(record => record[key])
  }
```

We are making some progress here. We constrain the key so that it is only an existing key on the object. Typescript infers the return type is not specific enough.

If you mouse over the function, the return type is `T[keyof T][]`
In this case, if there are four keys with values of type `number` and `string`, the compiler infers the returned `array` as a `string` or number `array`.

It can be better.

### Version 5:
```typescript
  function pluck<T, K extends keyof T>(records: T[], key: K): T[K][]{
    return records.map(record => record[key])
  }
```

Perfecto. In this final function, we have a generic function with two parameters. `T` and `K` which denotes the object and a key of the object respectively. The second parameter K is a subset of `keyof T`.

We constrain key so that it is only a key that exists on the object.

Using this variant, we get straightforward autocomplete of the known properties in the object.

Here is a concrete example:

![live demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t7ohisslvhwl2htvgivg.gif)

Using the `TS` playground, we see that we get nice autocomplete.

Check it out in the [Live playground](https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABABwDYggawDwBUA0iA0ogKYAeUpYAJgM6KakCecwiuAfABQBOpEOL3oAuDgG0AuoSbMRRAJRjc4opKkBvALAAoRIn5QQvJP0HC6AOgC2AQ2R8BQmogC8nA0+HjZkhboBfXV0oZmRSRAAlLxoYMABzXDCI10QAIjojWLg0xAAfdNQYADdSNOCdUPComKTq1O09fURbXlhMsUzeOPj8XWbEWChUUk6oboS+pv1+Eds6UgARWyoxZaopgbNnHrrRmvNYhL3+xCCdXUEwTJbeXjFow72pN0RxU8aB1vaoMTTSGhpTbNIYjP7hXjAARQIGnGakOYLdb7MCkADuiGR3AUwPhh12yT+mRA2VhTQCwM+zW+MA66QAXgCyQNQfs0szmrNSPMlisUejMXzsbjPPjjoTCiUysCKR84bcfn96XE4CM6HRMMwOfpWX8wHBENYhBErrFYAhtZ5EbzVohURisTj5dthATwkSsvAObKdJIANwVK43GmZBipNAYTDcVq8QgAchDUDj-h0Qagop24vCYZQ6Cw0bu8ZdR0SyWTQA)

## Conclusion

Typescript offers a lot of quality of life improvement for DX. Even though it can be tempting to throw any all over the place, using precise types benefits far outweighs the trouble of thinking a bit more about our types.

The effective typescript book is great. Check it out as well [here](https://effectivetypescript.com/)