How to implement TypeScript with deep partial mapping without violating array properties

Any ideas how you can recursively apply TypeScript Partial mapped type to an interface without violating any keys with arrays returned by types?

The following approaches were not sufficient:

interface User {  
  emailAddress: string;  
  verification: {
    verified: boolean;
    verificationCode: string;
  }
  activeApps: string[];
}

type PartialUser = Partial<User>; // does not affect properties of verification  

type PartialUser2 = DeepPartial<User>; // breaks activeApps' array return type;

export type DeepPartial<T> = {
  [ P in keyof T ]?: DeepPartial<T[ P ]>;
}

Any ideas?

UPDATE: The accepted answer is a better and more general solution so far.

A temporary workaround has been found that involves the intersection of types and two mapped types as follows. The most notable drawback is that you must provide property overrides to restore dirty keys that have return array types.

for instance

type PartialDeep<T> = {
  [ P in keyof T ]?: PartialDeep<T[ P ]>;
}
type PartialRestoreArrays<K> = {
  [ P in keyof K ]?: K[ P ];
}

export type DeepPartial<T, K> = PartialDeep<T> & PartialRestoreArrays<K>;

interface User {  
 emailAddress: string;  
 verification: {
   verified: boolean;
   verificationCode: string;
 }
 activeApps: string[];
}

export type AddDetailsPartialed = DeepPartial<User, {
 activeApps?: string[];
}>

Like this

+18
3

2018-06-22:

, , TypeScript 2.8. . , @krzysztof-kaczor , , TypeScript 2.8 .


, , ( TypeScript 2.4 ), , , :

-, :

type False = '0'
type True = '1'
type Bool = False | True
type IfElse<Cond extends Bool, Then, Else> = {'0': Else; '1': Then;}[Cond];

, , , IfElse<True,A,B> A IfElse<False,A,B> B

Rec<K,V,X>, K V, Rec<K,V,True> , , Rec<K,V,False> :

type Rec<K extends string, V, Required extends Bool> = IfElse<Required, Record<K, V>, Partial<Record<K, V>>>

User DeepPartialUser. UserSchema<R> , , , , R True False:

type UserSchema<R extends Bool> =
  Rec<'emailAddress', string, R> &
  Rec<'verification', (
    Rec<'verified', boolean, R> &
    Rec<'verificationCode', string, R>
  ), R> &
  Rec<'activeApps', string[], R>

, ? User DeepPartialUser :

interface User extends UserSchema<True> { } // required
interface DeepPartialUser extends UserSchema<False> { }  // optional

:

var user: User = {
  emailAddress: 'foo@example.com',
  verification: {
    verified: true,
    verificationCode: 'shazam'
  },
  activeApps: ['netflix','facebook','angrybirds']
} // any missing properties or extra will cause an error

var deepPartialUser: DeepPartialUser = {
  emailAddress: 'bar@example.com',
  verification: {
    verified: false
  }
} // missing properties are fine, extra will still error

. , !

+6

TS 2.8 :

type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends Array<infer U>
    ? Array<DeepPartial<U>>
    : T[P] extends ReadonlyArray<infer U>
      ? ReadonlyArray<DeepPartial<U>>
      : DeepPartial<T[P]>
};

https://github.com/krzkaczor/ts-essentials .

+47

You can use ts-toolbelt , it can perform operations on types at any depth

In your case, it will be:

import {O} from 'ts-toolbelt'

interface User {  
    emailAddress: string;  
    verification: {
      verified: boolean;
      verificationCode: string;
    }
    activeApps: string[];
}

type optional = O.Optional<User, keyof User, 'deep'>

And if you want to deeply calculate it (for display purposes), you can use for this Compute

0
source

Source: https://habr.com/ru/post/1682488/


All Articles