13

Let's say I have a type like this:

type User = {
  uid: string,
  displayName?: string,
  bestFriend?: string,
}

Is it possible to extract the optional properties from my User type using a mapped type of some kind? I'm looking for how to define the OptioanlProperties<T> type below.

type OptionalUserProperties = OptionalProperties<User>
// type OptionalUserProperties = "displayName" | "bestFriend"

My use-case is to compute an UpdateOf<User> type that permits specific "operation" values, like DeleteProperty to be assigned to keys that are optional in the base type.

export type UpdateOf<T> =
  // optional fields may be their own type, or the special DeleteProperty type.
  { [P in OptionalProperties<T>]?: T[P] | DeleteProperty } &
  // required fields may be changed, but cannot be deleted.
  { [P in Diff<keyof T, OptionalProperties<T>>]?: T[P] }
Just Jake
  • 4,698
  • 4
  • 28
  • 33

1 Answers1

41

Yes:

type OptionalPropertyOf<T extends object> = Exclude<{
  [K in keyof T]: T extends Record<K, T[K]>
    ? never
    : K
}[keyof T], undefined>
Karol Majewski
  • 23,596
  • 8
  • 44
  • 53
  • 4
    This is much more concise than the solution in the linked duplicate question. Thank you. – Just Jake Dec 22 '18 at 23:37
  • 3
    Indeed this solution feels better as well. – wcandillon Aug 15 '19 at 07:42
  • 1
    This is amazing what one can do with TypeScript! I needed this to improve my [`ModifyDeep`](https://stackoverflow.com/a/74216680/985454) to allow changing of property optionality of an existing type. – Qwerty May 30 '23 at 13:42