Introducing FillKeys Utility Type for Easier Destructuring of Discriminated Unions in TypeScript
Tags: typescript
Published: 2023-04-07 02:00:56

---
title: Introducing FillKeys Utility Type for Easier Destructuring of Discriminated Unions in TypeScript
published: true
description: 
tags: typescript
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-04-07 01:58 +0000
---

In TypeScript development, discriminated unions are incredibly useful, but they can pose some problems when trying to destructure objects. In this article, I'll discuss the issues and the solution using the FillKeys utility type.

## The Problem

As demonstrated in the following sample code, attempting to destructure a union type of `Success` and `Failure` results in a compilation error.

```typescript
type Success = {
  type: "success";
  value: number;
};
type Failure = {
  type: "failure";
  error: Error;
};

declare const result: Success | Failure;
const { type, value, error } = result; // Cannot destructure
// Property 'value' does not exist on type 'Success | Failure'.(2339)
// Property 'error' does not exist on type 'Success | Failure'.(2339)
```


## The Solution

We can solve the above problem using the FillKeys utility type. Modify the `result` type to `FillKeys<Success | Failure>` and perform the destructuring as follows.

```typescript
type FillKeys<T> = (
  (T extends T ? keyof T : never) extends infer AllKeys
    ? T extends T
      ? { [K in keyof T]: T[K] } & {
          [K in AllKeys extends keyof T
            ? never
            : AllKeys extends string
            ? AllKeys
            : never]?: undefined;
        }
      : never
    : never
) extends infer T
  ? { [K in keyof T]: T[K] }
  : never;

declare const result: FillKeys<Success | Failure>;
const { type, value, error } = result; // Destructuring is now possible! No compilation errors
```



When using the FillKeys utility type to transform `Success | Failure`, the resulting type becomes:

```typescript
{
    type: "success";
    value: number;
    error?: undefined;
} | {
    type: "failure";
    error: Error;
    value?: undefined;
}
```



This type considers both `Success` and `Failure` types, and properties not present in each type are added as optional with `undefined`. This prevents compilation errors when performing destructuring.

Specifically, the `Success` type does not have an `error` property, so `error?: undefined;` is added. Similarly, the `Failure` type does not have a `value` property, so `value?: undefined;` is added.

This makes destructuring discriminated unions more straightforward, and type narrowing still works.

```typescript
declare const result: FillKeys<Success | Failure>;
const { type, value, error } = result;
if (type === "success") {
  value * 100;
} else {
  error.message;
}
```


## Conclusion

When working with discriminated unions, you might encounter difficulties with destructuring. However, by using the FillKeys utility type, you can overcome these issues, making destructuring more manageable. As type narrowing also works, development efficiency is improved. The FillKeys utility type is a valuable tool for TypeScript developers using discriminated unions.