TypeScript需要一个参数或另一个参数,但两者都不需要

13 typescript tsc typescript3.0

说我有这种类型:

export interface Opts {
  paths?: string | Array<string>,
  path?: string | Array<string>
}
Run Code Online (Sandbox Code Playgroud)

我想告诉用户他们必须传递路径或路径,但没有必要通过它们.现在的问题是这个编译:

export const foo = (o: Opts) => {};
foo({});
Run Code Online (Sandbox Code Playgroud)

有谁知道允许2个或更多可选但至少1是TS的必要参数?

Her*_*ers 12

你可以用

export type Opts = { path: string | Array<string> } | { paths: string | Array<string> }
Run Code Online (Sandbox Code Playgroud)

为了提高可读性,您可以写:

type StringOrArray = string | Array<string>;

type PathOpts  = { path : StringOrArray };
type PathsOpts = { paths: StringOrArray };

export type Opts = PathOpts | PathsOpts;
Run Code Online (Sandbox Code Playgroud)

  • 如果你想 - 偏离你原来的问题,正好是一个,一个或多个路径,使用类型 PathOpts = { path: StringOrArray, paths: never }; 并输入 PathsOpts = { 路径:StringOrArray,路径:从不}; 谢谢你,提香,指出这一点。 (4认同)
  • 请注意,由于多余的属性检查如何在联合上工作,根据您的定义,这也是一个有效的调用:`foo({ path: "", paths: ""})` 我认为 op 不需要。 (2认同)

Tit*_*mir 6

如果您已经定义了该接口并且希望避免重复声明,则可以选择创建一个带有类型的条件类型,并返回包含一个字段的union中每个类型的并集(以及never值的记录)任何其他字段都不能指定任何额外的字段)

export interface Opts {
    paths?: string | Array<string>,
    path?: string | Array<string>
}

type EitherField<T, TKey extends keyof T = keyof T> =
    TKey extends keyof T ? { [P in TKey]-?:T[TKey] } & Partial<Record<Exclude<keyof T, TKey>, never>>: never
export const foo = (o: EitherField<Opts>) => {};
foo({ path : '' });
foo({ paths: '' });
foo({ path : '', paths:'' }); // error
foo({}) // error
Run Code Online (Sandbox Code Playgroud)

编辑

关于这里使用的魔术类型的一些细节.我们将使用条件类型分布属性来实际迭代该T类型的所有键.分布式属性需要一个额外的类型参数才能工作,我们TKey为此目的引入,但我们还提供了所有键的默认值,因为我们想要获取所有类型的键T.

因此,我们要做的是实际获取原始类型的每个键,并创建一个仅包含该键的新映射类型.结果将是包含单个键的所有映射类型的并集.映射类型将删除属性的可选性(此处-?描述),该属性与()中的原始属性的属性相同.TT[TKey]

需要解释的最后一部分是Partial<Record<Exclude<keyof T, TKey>, never>>.由于对对象文字的多余属性检查有效,我们可以在分配给它的对象键中指定联合的任何字段.这是一个联盟,例如{ path: string | Array<string> } | { paths: string | Array<string> }我们可以分配这个{ path: "", paths: ""}不幸的对象文字.解决方案是要求如果任何给定的联合成员的对象文字中存在任何其他属性T(除此之后TKey我们得到Exclude<keyof T, TKey>),它们应该是类型never(所以我们得到Record<Exclude<keyof T, TKey>, never>>).但我们不希望必须never为所有成员明确指定,这就是我们Partial之前记录的原因.