Dependent Function Arguments With TypeScript
Tags: typescript, webdev, tutorial, dependent
Published: 2022-11-04 19:01:03

Typescript is an excellent tool for making sure we have fewer bugs by having the IDE complete our code in accordance with the types we defined.

Here we'll see how to use mapped types to have more control over our functions while making them more readable for the next developer that uses those functions.


```ts
const enum DATA_TYPE {
  STYLE = 'STYLE',
  THEME = 'THEME'
}

const enum STYLE_PROPERTY {
  ID = 'ID',
  NAME = 'NAME',
  DATE = 'DATE'
}

const enum THEME_PROPERTY {
  ID = 'ID',
  NAME = 'NAME',
  LAST_MODIFIED = 'LAST_MODIFIED',
  ORIGIN_THEME = 'ORIGIN_THEME',
  PROTECTED = 'PROTECTED'
}

interface ListGetTypeMap {
  [DATA_TYPE.STYLE]: STYLE_PROPERTY[];
  [DATA_TYPE.THEME]: THEME_PROPERTY[];
}

interface ObjectGetTypeMap {
  [DATA_TYPE.STYLE]: {
    [STYLE_PROPERTY.ID]?: string;
    [STYLE_PROPERTY.NAME]?: string;
    [STYLE_PROPERTY.DATE]?: string;
  };
  [DATA_TYPE.THEME]: {
    [THEME_PROPERTY.ID]?: string;
    [THEME_PROPERTY.NAME]?: string;
    [THEME_PROPERTY.LAST_MODIFIED]?: string;
    [THEME_PROPERTY.ORIGIN_THEME]?: string;
    [THEME_PROPERTY.PROTECTED]?: boolean;
  };
}

type TGetProperty = <T extends keyof ListGetTypeMap>(type: T, keys: ListGetTypeMap[T]) => ObjectGetTypeMap[T];
```

## Try it out in [this Typescript Playground] (https://www.typescriptlang.org/play?#code/MYewdgzgLgBApmArgWxgEQIIBUMH0sCaACgKIwDeAUDDAMoCKAqiSQFpkC8MA5A82yW4AaajCwAJEgFlOPCdMGUAvpUqhIsBCjpMW7XEQBKAeVKHCFUQEk0MLtxvDRAOQwy7PVzKc1MWWdx+iipq4NDwSKjyMgYmZhZUNDYeDmg+MF4BmekAMhi0WLhSxmhWAGJWJLb2eQVFJeWVaSI0xoZWAOJWzviS7vZtnd29CulGxv4Awv7VPONTM9zKqnAAHgAOIABOsACWYFBwWwBmAIbAcDA5u9AdcFBYAJ7rcFKn65Y0ANp+eISkADo+HoSABdABcOn4+nG8QIX1BAG5RD9sH9iCQAdEwZDsbFTCRzPCkctKGtNjsYPtDidzpdjAAjABWcGAUDuD2er3enxgqJw+AxQN0AghvO+wIE+LhAJsoIA-JDoFt9gBzZE0CUimFxQmEAGZBVKqAqsDq0Ra6EkaV6ggAoJGmDKtUamBKV389GA7FixKar542G22VoR3Os2u76B3VEg1uMGKp0ml0WvnRgmx2qFYqlCpVMPJiOpgN9a1B2ODLo9H2J8PmzVp0s22PzEjTfOJhkgEAAGzgpzArvdpPJ21gUC5Yg5RC2IBeO0eHgAPFh4KtDmAACYQGAAazgjxAxyuN3Z9yeLze6wAfAAKCcvXFCPcHiCQ663c9cq8B0EASjsa8YEZFk2Q5C9uXWX9kVCDQYGORAwGAABGDxbwAjggPIYdKFvBCkNQ04dywadZ3nCc-1vX5BUBSV2GfL46LLGN9SCZ90xlOU-2RPDEJQmAiKne4ZznI4KKotEaMxJiGKY5tWOwEhnzk8t9S4nj8P4wSSOEsixMeSjqP+TFsVk7VmIzBT-GU8z5LtdTcM0wjiNI0SF0MyTjKxUsGI44MbHYptVLtQ1uMoIA) 🎈

---

### Code Breakdown
Our goal here is to make sure the developer that uses a function of type `TGetProperty`, will have Typescript guard the arguments he gives that function.

In our case, we want to make sure that every item in the `keys` array is **either** of type `STYLE_PROPERTY[]` **or** `THEME_PROPERTY[]`.
By defining the `type` argument as `DATA_TYPE.STYLE` **or** `DATA_TYPE.THEME`
we get the type of the `keys` argument.

#### This is a scalable solution
If we want to add another enum like `CONTENT_PROPERTY` for example, all we need to do is add it to `ListGetTypeMap` and `ObjectGetTypeMap`; and we have support for it too 🎇


### Let's see some examples 
Say `func1` is a function that is of type `TGetProperty`.

#### Bad examples ⛔

![First bad example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gb3m5qv9at32eyem7sxy.png)

When passing `DATA_TYPE.STYLE` to the `type` argument, Typescript expects the next argument to be of type `STYLE_PROPERTY` **only**,
and the error message is pretty clear too so that's cool 🆒

![Second bad example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3p2x2a88sdagzrs9c5m9.png)

Same here, passing `DATA_TYPE.THEME` to `type` will make Typescript reject an array with items of type `STYLE_PROPERTY`, and **only** accept items of type `THEME_PROPERTY`.

#### Good examples ✅


![Good examples](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9zv8cnu3o2paxi1qoy0h.png)

---

### Conclusion
Use Typescript to help set guards and guidelines for the next developer that will use the functions you wrote.
This will result in fewer bugs and overall more productive coding.