24

If I have a type with all required properties, how can I define another type with the same properties where some of it's properties are still required but the rest are optional?

For example I want a new type derived from SomeType where prop1 and prop2 are required but the rest are optional:

interface SomeType {
    prop1;
    prop2;
    prop3;
    ...
    propn;
}

interface NewType {
    prop1;
    prop2;
    prop3?;
    ...
    propn?;
}
spb
  • 897
  • 2
  • 7
  • 15

4 Answers4

58

You can use combination of Partial and Pick to make all properties partial and then pick only some that are required:

interface SomeType {
    prop1: string;
    prop2: string;
    prop3: string;
    propn: string;
}

type OptionalExceptFor<T, TRequired extends keyof T> = Partial<T> & Pick<T, TRequired>

type NewType = OptionalExceptFor<SomeType, 'prop1' | 'prop2'>

let o : NewType = {
    prop1: "",
    prop2: ""
}
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • 11
    For completeness: `type RequiredExceptFor = Pick> & Partial;` with `type Diff = T extends U ? never : T;` – toancaro Mar 11 '19 at 16:01
  • 1
    There's a catch here. If the source properties are optional, say prop1 and prop2, that won't trigger any error. Wrapping Pick in Required shold do the job. `type OptionalExceptFor = Partial & Required>;` – AlexanderD Sep 22 '21 at 11:26
  • 1
    Thanks @AlexanderD. But for this one, I want only to mark optional what properties I want, so the rest remains the same. – Charles-Lévi BRIANI Dec 16 '22 at 13:45
5

For tetter performance, in addition to Titian's answer, you must do :

type OptionalExceptFor<T, TRequired extends keyof T = keyof T> = Partial<
  Pick<T, Exclude<keyof T, TRequired>>
> &
  Required<Pick<T, TRequired>>;
  • this works for me when I tried to use the type in a spread operator into a fully required version, there Titian's above did not. – JBCP Sep 10 '22 at 05:06
  • In what situations would this affect performance? Would it be compile times for typescript? Or handling big objects in editor? As I guess it has no impact on js execution? – Cwista Dec 09 '22 at 19:48
3
type onlyOneRequired = Partial<Tag> & {id: string}

In the above type, all properties of Tag are optional instead of id which is required. So you can use this method and specify the required properties.

M Fuat
  • 1,330
  • 3
  • 12
  • 24
  • This solution does not work : https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgLIQM4bgcxQbwChkTlgATALmRAFcBbAI2gBpjT7Ns9r9kEA9uAjhqGMFFA4WyKBAA2cSOQCSVZOMkgcAbQC6yAD41a8+cgC+bUsji0wACwFQxEqddII5S4EIAiShDUAZBsFoSE8hBgyOTAGAAOigCeEOQA+pxYuJjUAIJQUHDJADyEAJAACnBQYMBw8iXo2XgAfMgAZMhE5eUU1HRM0BXldo7Orlo4I14QPv6BwYEjWdxB3SPlgsKiGm7aI+Hl4a0RcYkpaZlcORjIALzI+oTnScVXq7cPTxU95Z88br8ISQXYAIgA7g5AgA3aBgyxsXpjJwuZBggBSAgcIGQfgEEDBSK23jqC0gAwgELxgQAFABKJFWCoAOjZtNelwyAMwAH5YvE3qluTc8HdqPpGYQ9BFthgBFEWfIBDgOYKudcWpgdAAGPQsnksuSKZRqelAA – cassepipe Apr 20 '23 at 13:30
  • My bad, it does not work with *nested* types – cassepipe Apr 20 '23 at 15:17
  • @cassepipe Sorry I was on a holiday I hope the answer helped you :) – M Fuat Apr 30 '23 at 14:56
  • @M Fuat It did thanks. Actually I believe none of the other answers can work with *nested* types anyways. Your answer should be the accepted one as it is the most readable in my opinion – cassepipe Apr 30 '23 at 15:32
2

From this article:

We can derive the following type:

export type PartialPick<T, F extends keyof T> = Omit<T, F> & Partial<Pick<T, F>>;

F - the fields that should be made optional

T - the type

In your example:

type NewType = PartialPick<SomeType, 'prop3' | ... | 'propn'>

If you'd like to make some properties required instead of the partial use:

type RequiredPick<T, F extends keyof T> = Omit<T, F> & Required<Pick<T, F>>;

If NewType should be an interface and has additional fields besides the fields in SomeType, you can use extends from the type:

interface SomeType {
    prop1;
    prop2;
    prop3;
    prop4;
}
interface NewType extends PartialPick<SomeType, 'prop3' | 'prop4'> {
    prop5;
}
Noy Oliel
  • 1,430
  • 3
  • 13
  • 26