使用条件和映射类型保留字段可选性

问题描述 投票:0回答:1

以下类型从输入类型的所有嵌套深度中删除所需的属性:

type DeepRemoveProps<T, P extends string> = T extends object
  ? { [K in Exclude<keyof T, P>]: DeepRemoveProps<T[K], P> }
  : T;

例如:

type Input = {
  a: {
    b: string,
    c: {
      xx: 'removed',
      d: string
    }
  },
  yy: { removed: true },
  xx: 'im removed'
};
type Result = DeepRemoveProps<Input, 'xx' | 'yy'>;

在这种情况下

Result
决定:

type Result = {
  a: {
    b: string,
    c: {
      d: string
    }
  }
};

但是这种类型无法保留字段是否可选!

例如,使用这种类型:

type Input = {
  a: {
    b?: string,
    c: {
      xx: 'removed',
      d?: string
    }
  },
  yy: { removed: true },
  xx: 'im removed'
};

DeepRemoveProps<Input, 'xx' | 'yy'>
的结果与上面完全相同,具有所需的所有属性 - 由于某种原因,
Input['a']['b']
Input['a']['c']['d']
没有保留其
?
。我的印象是
DeepRemoveProps
中使用的映射类型默认会保留
?
是否存在。

如何以保留字段是否可选的方式实现

DeepRemoveProps

typescript ternary mapped-types
1个回答
0
投票

如果您希望映射类型保留要映射的属性的可选和

readonly
修饰符,则需要编写同态映射类型(请参阅“同态映射类型”是什么意思?
{[⋯ in keyof T]: ⋯}
表示 generic
T
,其中
in keyof
直接出现。你有
in Exclude<keyof
,它打破了它。 (TypeScript 需要知道原始类型是什么,以便复制属性修饰符。如果它看到
in keyof T
,它将了解您正在从
T
复制。但是,如果
in
之后设置的键是某种任意类型,则只是取决于在某处
keyof T
,它不能确定。)

我的建议是使用 as

 进行键重新映射,并过滤掉 as
 子句中的键,而不是 
in
 子句中的键。  也就是说,使用 
Exclude
 来作用于 
K
 而不是 
keyof T
:

type DeepRemoveProps<T, P extends string> = T extends object ? { [K in keyof T as Exclude<K, P>]: DeepRemoveProps<T[K], P> } : T;
这会将与 

K

 重叠的任何键 
P
 映射到 
never
,从而具有从输出中抑制该键的效果。  现在您的代码应该按预期运行:

type Result = DeepRemoveProps<Input, 'xx' | 'yy'>; /* type Result = { a: { b?: string | undefined; c: { d?: string | undefined; }; }; } */

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.