【问题标题】:Define an object with an optional amount of keys, but at least ONE key使用可选数量的键定义一个对象,但至少有一个键
【发布时间】:2020-07-01 12:41:09
【问题描述】:

我怎样才能做到这一点?

type Fruit = "apple" | 'banana' | 'coconut'

type FruitCollection = { [f in Fruit]?: number }

const validFruitCollection: FruitCollection = { apple: 1, coconut: 2 } 

const emptyCollectionShouldNotPass: FruitCollection = {} // I don't want typescript to let this pass

【问题讨论】:

标签: typescript


【解决方案1】:

我们需要的是排除空对象可能性的类型。为了实现这一点,我们需要实用程序类型和值构造函数。考虑:

type Fruit = "apple" | 'banana' | 'coconut'

type FruitCollection = { [f in Fruit]?: number }

// type which will exclude empty object
type NotEmpty<T> = {} extends T ? never : T

// value constructor
const makeFruitCollection = <T extends FruitCollection>(c: NotEmpty<T>) => c; 

// use cases
const validFruitCollection = makeFruitCollection({ apple: 1, coconut: 2 }) // ok ?
const emptyCollectionShouldNotPass = makeFruitCollection({}) // error ?

类型NotEmpty正在检查我们已经通过FruitCollection的所有需求的T是否不是空对象,如果是,我们得到never,并且没有never类型的值,因此使用函数{} 将无法编译。

The Playground

【讨论】:

  • 如果您使用的是函数,那么这是一个不错的选择,此解决方案的常见问题是类型本身不需要至少一个属性,因此您最终可以得到满足以下条件的变量不尊重约束。但是对于函数参数,这是一个很好的,可以说更容易理解的解决方案:)
  • 有一个内置的实用程序类型:排除。因此NotEmpty&lt;T&gt; 可以重写为Exclude&lt;T, {}&gt;?
【解决方案2】:

您可以将类型与所有可选成员与所有属性的联合相交,其中联合的每个组成部分都需要一个成员。所以基本上你将拥有:


type WhatWeWant = {
    apple?: number | undefined;
    banana?: number | undefined;
    coconut?: number | undefined;
} & (
    | { apple: number; }
    | { banana: number; }
    | { coconut : number ;})

要在不写出来的情况下获得这种类型,我们可以使用映射类型:


type RequireOne<T> = T & { [P in keyof T]: Required<Pick<T, P>> }[keyof T]
type FruitCollection = RequireOne<{ [f in Fruit]?: number }>

Playground Link

RequireOne 中映射类型的想法是在上面的WhatWeWant 类型中创建联合(T 将是原始类型,将是所有可选属性)。所以我们要做的是,在映射类型中,我们将T 中的每个属性输入为Required&lt;Pick&lt;T, P&gt;&gt;。这意味着对于每个键,我们都会得到一个只包含该键的类型,例如,基本上是这种类型:

{
  apple: { apple: number; }
  banana: { banana: number; }
  coconut: { coconut: number ;}
}

对于这种类型,获得我们想要的联合的问题只是通过索引keyof T 来获得我们对象中所有属性类型的联合。

【讨论】:

  • @Lev 添加了解释,如果您需要更多详细信息,请告诉我
  • 非常感谢。试着看看我的关节头现在是否得到它。
猜你喜欢
  • 2022-07-07
  • 2019-02-09
  • 2014-10-10
  • 2018-01-02
  • 2021-04-17
  • 1970-01-01
  • 2021-01-31
  • 2022-01-13
相关资源
最近更新 更多