如何创建类似于Partial的,需要设置单个属性

Jua*_*des 16 generics typescript

我们的结构如下:

export type LinkRestSource = {
    model: string;
    rel?: string;
    title?: string;
} | {
    model?: string;
    rel: string;
    title?: string;
} | {
    model?: string;
    rel?: string;
    title: string;
};
Run Code Online (Sandbox Code Playgroud)

这几乎与说法相同

type LinkRestSource = Partial<{model: string, rel: string, title: string}>
Run Code Online (Sandbox Code Playgroud)

除了这将允许传入空对象,而初始类型需要传递其中一个属性

我怎样才能创建类似的泛型Partial,但其行为与上面的结构相似?

jca*_*alz 31

我想我有一个解决方案.你正在寻找一种类型T并生成相关类型的东西,其中包含至少一个属性T.也就是说,它就像是Partial<T>排除了空物体.

如果是这样,这里是:

type AtLeastOne<T, U = {[K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U]
Run Code Online (Sandbox Code Playgroud)

要对它进行解剖:首先AtLeastOne<T>Partial<T>交叉的东西. U[keyof U]意味着它是所有属性值的并集U.和我定义(默认值)U是一个映射类型,其中的每个属性T被映射到Pick<T, K>,单属性类型的关键K.(例如,Pick<{foo: string, bar: number},'foo'>相当于{foo: string}......它'foo'从原始类型中"选取" 属性.)这意味着U[keyof U]在这种情况下是所有可能的单属性类型的并集T.

嗯,这可能会令人困惑.让我们一步一步地看看它如何在以下具体类型上运行:

type FullLinkRestSource = {
  model: string;
  rel: string;
  title: string;
}

type LinkRestSource = AtLeastOne<FullLinkRestSource>
Run Code Online (Sandbox Code Playgroud)

这扩大到了

type LinkRestSource = AtLeastOne<FullLinkRestSource, {
  [K in keyof FullLinkRestSource]: Pick<FullLinkRestSource, K>
}>
Run Code Online (Sandbox Code Playgroud)

要么

type LinkRestSource = AtLeastOne<FullLinkRestSource, {
  model: Pick<FullLinkRestSource, 'model'>,
  rel: Pick<FullLinkRestSource, 'rel'>,
  title: Pick<FullLinkRestSource, 'title'>
}>
Run Code Online (Sandbox Code Playgroud)

要么

type LinkRestSource = AtLeastOne<FullLinkRestSource, {
  model: {model: string},
  rel: {rel: string},
  title: {title: string}>
}>
Run Code Online (Sandbox Code Playgroud)

要么

type LinkRestSource = Partial<FullLinkRestSource> & {
  model: {model: string},
  rel: {rel: string},
  title: {title: string}>
}[keyof {
  model: {model: string},
  rel: {rel: string},
  title: {title: string}>
}]
Run Code Online (Sandbox Code Playgroud)

要么

type LinkRestSource = Partial<FullLinkRestSource> & {
  model: {model: string},
  rel: {rel: string},
  title: {title: string}>
}['model' | 'rel' | 'title']
Run Code Online (Sandbox Code Playgroud)

要么

type LinkRestSource = Partial<FullLinkRestSource> &
  ({model: string} | {rel: string} | {title: string})
Run Code Online (Sandbox Code Playgroud)

要么

type LinkRestSource = {model?: string, rel?: string, title?: string} & 
  ({model: string} | {rel: string} | {title: string})
Run Code Online (Sandbox Code Playgroud)

要么

type LinkRestSource = { model: string, rel?: string, title?: string } 
  | {model?: string, rel: string, title?: string} 
  | {model?: string, rel?: string, title: string}
Run Code Online (Sandbox Code Playgroud)

我认为,这就是你想要的.

你可以测试一下:

const okay0: LinkRestSource = { model: 'a', rel: 'b', title: 'c' }
const okay1: LinkRestSource = { model: 'a', rel: 'b' }
const okay2: LinkRestSource = { model: 'a' }
const okay3: LinkRestSource = { rel: 'b' }
const okay4: LinkRestSource = { title: 'c' }

const error0: LinkRestSource = {} // missing property
const error1: LinkRestSource = { model: 'a', titel: 'c' } // excess property on string literal
Run Code Online (Sandbox Code Playgroud)

那么,这对你有用吗?祝好运!

  • 做得好!有趣的解决方案。TypeScript非常强大,不是吗? (3认同)
  • @JuanMendes这是`AtLeastTwo &lt;T,U = {[T中的K]:Pick &lt;T,K&gt;和AtLeastOne &lt;Omit &lt;T,K &gt;&gt;}&gt; = Partial &lt;T&gt;&U [keyof U]` (3认同)
  • 我认为应该在类似打字稿黑客资料库的东西中,非常强大的解决方案 (2认同)
  • 这是所有N个属性的来源:https://gist.github.com/Dykam/2272e696435ee3e37012b065365cedf5 (2认同)

小智 28

如果您知道想要哪些属性,还有另一种解决方案。

AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K>
Run Code Online (Sandbox Code Playgroud)

这也将允许您锁定一个类型的多个键,例如AtLeast<T, 'model' | 'rel'>.

  • 请参阅https://www.typescriptlang.org/play/#code/C4TwDgpgBAgsAyECGBnYAeAKgGigaSggA9gIA7AExSgGsIQB7AMykwD4oBeKABSQCdgASyQAbLBwBkvIQGMaWXHjYBuAFBrQkKPCFkaAJQhoAygwCu-WdG5xEqDAG8AtgwoRRALihp+egOa4 -B7evgG4wsCiEKHAfmT+AL64AESu7qIPUAA+UCnBmaoasgxkaFCieobGwGaW1t66+kamFly2UC5uIXkgEMApiepAA (2认同)

Gaa*_*far 12

jcalz 提供的更简单的解决方案版本:

type AtLeastOne<T> = { [K in keyof T]: Pick<T, K> }[keyof T]

所以整个实现变成

type FullLinkRestSource = {
  model: string;
  rel: string;
  title: string;
}

type AtLeastOne<T> = { [K in keyof T]: Pick<T, K> }[keyof T]
type LinkRestSource = AtLeastOne<FullLinkRestSource>

const okay0: LinkRestSource = { model: 'a', rel: 'b', title: 'c' }
const okay1: LinkRestSource = { model: 'a', rel: 'b' }
const okay2: LinkRestSource = { model: 'a' }
const okay3: LinkRestSource = { rel: 'b' }
const okay4: LinkRestSource = { title: 'c' }

const error0: LinkRestSource = {} // missing property
const error1: LinkRestSource = { model: 'a', titel: 'c' } // excess property on string literal
Run Code Online (Sandbox Code Playgroud)

这是 TS 游乐场链接来尝试它


Teo*_*oro 5

不幸的是,上述答案对我不起作用。
要么是因为编译器无法捕获错误,要么是因为我的 IDE 无法检索对象的预期属性,即使它的类型已注释。

以下内容完美运行,取自官方microsoft azure/keyvault-certificates 包:

type RequireAtLeastOne<T> = { [K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>; }[keyof T]
Run Code Online (Sandbox Code Playgroud)