5

There is a type

{ a: string, b: number, c?: string, d?: number }

How to get the type

'c' | 'd'

from this?

Kirill A
  • 441
  • 1
  • 5
  • 10

1 Answers1

6

Finally I found the solution based on Typescript how to create type with common properties of two types?

type MappedC<A, B> = {
  [K in keyof A & keyof B]:
  A[K] extends B[K]
    ? never
    : K
};

type OptionalKeys<T> = MappedC<T, Required<T>>[keyof T];

But it works like a magic, because when combining these two types into one and replacing B by Required it stops working.

Other solution, which seems to be more stable:

export type KeysOfType<T, U> = { [K in keyof T]: T[K] extends U ? K : never }[keyof T];
export type RequiredKeys<T> = Exclude<KeysOfType<T, Exclude<T[keyof T], undefined>>, undefined>;
export type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>;
Kirill A
  • 441
  • 1
  • 5
  • 10
  • 3
    These are actually different solutions. Take for example this: ```ts type Foo = { a: string; b: string | undefined; c?: string }; ``` The former solution (based on `Mapped`) will return (the expected): `'c'`. The latter solution (based on `RequiredKeys`) will return (the unexpected): `'b' | 'c'`. – Devin Feb 06 '19 at 21:39