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

问题描述 投票:13回答:3

说我有这种类型:

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

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

export const foo = (o: Opts) => {};
foo({});

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

typescript tsc typescript3.0
3个回答
12
投票

你可以用

export type Opts = { path: string | Array<string> } | { paths: string | Array<string> }

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

type StringOrArray = string | Array<string>;

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

export type Opts = PathOpts | PathsOpts;

5
投票

如果您已经定义了该接口并且希望避免重复声明,则选项可以是创建一个条件类型,该类型接受一个类型并返回一个union,其中包含一个字段的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

编辑

关于这里使用的魔术类型的一些细节。我们将使用distributive property of conditional types实际迭代T类型的所有键。分配属性需要额外的类型参数才能工作,我们为此目的引入TKey,但我们还提供所有键的默认值,因为我们想要获取T类型的所有键。

因此,我们要做的是实际获取原始类型的每个键,并创建一个仅包含该键的新映射类型。结果将是包含单个键的所有映射类型的并集。映射类型将删除属性的可选性(-?,描述为here),该属性与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以前的记录。


3
投票

这有效。

它接受一个通用类型T,在你的情况下是string

泛型类型OneOrMore定义了T中的1个或T的数组。

您的通用输入对象类型Opts要么是path的密钥OneOrMore<T>,要么是paths的密钥OneOrMore<T>。虽然没有必要,但我明确指出,唯一的其他选择永远不可接受。

type OneOrMore<T> = T | T[];

export type Opts<T> = { path: OneOrMore<T> } | { paths: OneOrMore<T> } | never;

export const foo = (o: Opts<string>) => {};

foo({});

{}发生错误

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