user606521
user606521

Reputation: 15434

How to make all keys of a type required while keeping undefined as possible value?

I want to map key/values of a type as follows:

interface Person {
  firstName: string | undefined;
  middleName?: string;
  lastName?: string | undefined;
}

// What I need:
// interface Person {
//   firstName: string | undefined;
//   middleName: string | undefined;
//   lastName: string | undefined;
// }

type Normalize<T> = { [K in keyof T]-?: T[K] };

// What I get with Normalize<Person>:
// interface Person {
//   firstName: string | undefined;
//   middleName: string;
//   lastName: string;
// }

const test: Normalize<Person> = {
  firstName: undefined,
  middleName: undefined, // ts err, type = string, undefined not allowed
  lastName: undefined, // ts err, type = string, undefined not allowed
};

Is it possible in typescript? It seems that -? is removing undefined from both key and value and I don't know how to add undefined value option if key is optional.

I read this Q/A Is it possible to make a property required, yet preserve undefined and I know about exactOptionalPropertyTypes compiler flag but it still does not solve how to convert key?: string to key: string | undefined.

Upvotes: 1

Views: 813

Answers (1)

colinD
colinD

Reputation: 2039

Not sure if this is the best solution, but you can achieve it with the following:

type AllOptionalKeys<T> = { [K in keyof T]-?: undefined extends T[K] ? K : never; }[keyof T];
type AllNonOptionalKeys<T> = { [K in keyof T]-?: undefined extends T[K] ? never : K; }[keyof T];

type Normalize<T> = { [K in AllOptionalKeys<T>]: T[K] | undefined } & { [K in AllNonOptionalKeys<T>]: T[K]; };

The two intermediate types allow to strip the ? from the keys, but keep track of which are optional.

Then it constructs an union type, where all optional keys have a value of T[K] | undefined, and all the non optional keys have T[K].

Upvotes: 3

Related Questions