如何在不破坏数组属性的情况下实现 TypeScript 深度部分映射类型 [英] How to implement TypeScript deep partial mapped type not breaking array properties

查看:75
本文介绍了如何在不破坏数组属性的情况下实现 TypeScript 深度部分映射类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于如何将 TypeScript 的 Partial 映射类型递归地应用于接口,同时不破坏具有数组返回类型的任何键的任何想法?

Any ideas as to how might apply TypeScript's Partial mapped type to an interface recursively, at the same time not breaking any keys with array return types?

以下方法还不够:

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 ]>;
}

有什么想法吗?

更新:已接受的答案 - 目前更好、更通用的解决方案.

UPDATE: Accepted answer - A better and more general solve for now.

找到了一个临时解决方法,它涉及类型和两个映射类型的交集,如下所示.最显着的缺点是您必须提供属性覆盖来恢复被污染的键,即具有数组返回类型的键.

Had found a temporary workaround which involves intersection of types and two mapped types as follows. The most notable drawback is that you have to supply the property overrides to restore sullied keys, the ones with array return types.

例如

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[];
}>

像这样

推荐答案

UPDATE 2018-06-22:

这个答案是在一年前写的,在令人惊叹的 条件类型 功能在 TypeScript 2.8 中发布.所以不再需要这个答案.请参阅下面@krzysztof-kaczor 的新答案,了解在 TypeScript 2.8 及更高版本中获得此行为的方法.

UPDATE 2018-06-22:

This answer was written a year ago, before the amazing conditional types feature was released in TypeScript 2.8. So this answer is no longer needed. Please see @krzysztof-kaczor's new answer below for the way to get this behavior in TypeScript 2.8 and up.

好的,这是我对疯狂但完全通用的解决方案(需要 TypeScript 2.4 及更高版本)的最佳尝试,这对您来说可能不值得,但如果您想使用它,请成为我的客人:

Okay, here is my best attempt at a crazy but fully general solution (requiring TypeScript 2.4 and up) which might not worth it to you, but if you want to use it, be my guest:

首先,我们需要一些类型级别的布尔逻辑:

First, we need some type-level boolean logic:

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

这里你需要知道的是 IfElse 类型的计算结果为 AIfElse 计算结果为 B.

All you need to know here is that the type IfElse<True,A,B> evaluates to A and IfElse<False,A,B> evaluates to B.

现在我们定义一个记录类型Rec,一个带有键K和值类型V的对象,其中Rec 表示该属性必需Rec 表示该属性>可选:

Now we define a record type Rec<K,V,X>, an object with key K and value type V, where Rec<K,V,True> means the property is required, and Rec<K,V,False> means the property is optional:

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


此时我们可以获取您的 UserDeepPartialUser 类型.让我们描述一个通用的 UserSchema,其中我们关心的每个属性要么是必需的,要么是可选的,这取决于 RTrue 还是 >假:


At this point we can get to your User and DeepPartialUser types. Let's describe a general UserSchema<R> where every property we care about is either required or optional, depending on whether R is True or 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>

丑吧?但是我们最终可以将 UserDeepPartialUser 描述为:

Ugly, right? But we can finally describe both User and DeepPartialUser as:

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


你去吧.希望有帮助!


There you go. Hope that helps!

这篇关于如何在不破坏数组属性的情况下实现 TypeScript 深度部分映射类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆