如果满足特定条件,映射类型能够将属性变为可选吗?

8
我试图创建一个映射类型,它将所有具有特定类型(在我的情况下为数组)的属性变成可选项,但是其他属性(非数组)则保持不变。我知道可以在定义映射类型时使用可选修饰符,例如:
type MyPartial<T> = {
  [key in keyof T]?: T[key]
}

但我不确定语法是否允许将此与条件结合使用。我可以通过以下方法接近:

type OptionalArrays<T> = {
  [key in keyof T]: T[key] extends Array<any> ? T[key] | undefined : T[key]
}

interface Example {
  foo: string[];
  bar: number;
};

type Example2 = OptionalArrays<Example>;

问题是这会导致显式的 undefined,而不是隐式的,所以尽管它对这两种情况的行为符合我的期望:
const value1: Example2 = {
  foo: [],
  bar: 3,
}

const value2: Example2 = {
  foo: undefined,
  bar: 3,
}

这会产生一个不必要的错误,指出缺少foo

const value3: Example2 = {
  bar: 3,
}

在映射类型中,是否可以添加可选修饰符(?),但只有在满足某些条件时才能添加?

Playground链接


这似乎是一个经过深思熟虑的问题,但我很难理解它的应用-您是否能够提供一个更具体的示例来说明它试图实现什么? - theMayer
1
我有一个预先存在的类型,其中一些属性是数组,而另一些则不是。我正在将这些对象写入一个不能表示空数组的数据库中,因此会使用undefined代替。因此,我的读写数据库的代码将使用可选数组版本。然后,一些简单的代码将用空数组填充这些缺失的值,以便客户端代码的其余部分不需要关心。 - Nicholas Tower
1个回答

13

可能有更优雅的方法,但是交叉两个映射类型并不太麻烦。一个用于可选属性,另一个用于必需属性。

例如:

type OptionalArrays<T> = {
  [key in keyof T as T[key] extends Array<any> ? key : never]?: T[key]
} & {
  [key in keyof T as T[key] extends Array<any> ? never : key]: T[key]
}

注意映射类型中键部分的as。这让您可以转换键类型。在这种情况下,如果我们想保留它,我们可以使用实际键,否则将其转换为neverPlayground
也许这个版本更相似,但可以说更容易理解一些。
type ArrayKeys<T> = {
    [key in keyof T]: T[key] extends Array<any> ? key : never
}[keyof T]

type OptionalArrays<T> =
    Omit<T, ArrayKeys<T>> & // get one type without array keys
    Partial<Pick<T, ArrayKeys<T>>> // get one type with array keys as optional

游乐场


我更新了我的答案,提供了第二个选项,它与第一个选项类似,但可能更容易解析。 - Alex Wayne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接