Typescript 类型脚本与联合的交集导致不存在属性
在下面的示例中,我定义了Typescript类型以从索引请求数据 有两种从索引服务器检索数据块的有效方法,通过startKey、endKey或通过startKey、limit(键数)检索 在组合这些备选案例来定义Typescript中的请求时,我做了一些错误的事情,我看不出是什么,除非我的相交联合的方法没有意义,或者我不理解Typescript错误Typescript 类型脚本与联合的交集导致不存在属性,typescript,compiler-errors,properties,intersection,discriminated-union,Typescript,Compiler Errors,Properties,Intersection,Discriminated Union,在下面的示例中,我定义了Typescript类型以从索引请求数据 有两种从索引服务器检索数据块的有效方法,通过startKey、endKey或通过startKey、limit(键数)检索 在组合这些备选案例来定义Typescript中的请求时,我做了一些错误的事情,我看不出是什么,除非我的相交联合的方法没有意义,或者我不理解Typescript错误 interface StartKey { startKey: string; } interface EndKey { endKey: s
interface StartKey {
startKey: string;
}
interface EndKey {
endKey: string;
}
interface Limit {
limit: number;
}
type KeyRange = StartKey & EndKey;
type KeyLimit = StartKey & Limit;
type KeyBounds = KeyRange | KeyLimit;
export type Options = {
someparam:string
} & KeyBounds;
function retrieve(options:Options){
const {
startKey,
endKey, //this line causes a compiler error
limit, //this line causes a compiler error
} = options;
}
首先,我创建了两个可选接口KeyRange(有endKey)和KeyLimit(有limit)。然后我将这些接口合并成一个KeyBounds类型。然后,在编写请求时,KeyBounds类型通过与其他索引请求特定参数的交集进行组合。例如,使用选项请求项目应能够使用一种或另一种策略来限制返回的结果
显示了我目前正在采取的方法,以及我从选项定义中得到的令人惊讶的错误
- 类型“Options”上不存在属性“endKey”
- 类型“Options”上不存在属性“limit”
interface KeyRange {
startKey:string
endKey?:string
limit?:string
}
function retrieve(range:KeyRange){
const {
startKey,
endKey,
limit,
} = range;
}
得到一个结果类型上根本不存在的错误(甚至不是可选的),这让我感到惊讶,并表明我错过了一些东西。有人能告诉我需要做什么才能使这些替代项有效吗?通常,您无法访问联合类型值上的属性,除非已知该属性键存在于联合的每个成员中:
interface Foo {
foo: string;
}
interface Bar {
bar: string;
}
function processFooOrBar(fooOrBar: Foo | Bar) {
fooOrBar.foo; // error!
// Property 'foo' does not exist on type 'Foo | Bar'.
// Property 'foo' does not exist on type 'Bar'
}
错误消息有点误导。当编译器抱怨“属性foo
在类型foo | Bar
上不存在”时,它实际上意味着“属性foo
在类型foo | Bar
的值中不存在”。属性当然可能存在,但由于Bar
类型的值不一定具有这样的属性,编译器会警告您
若您有一个联合类型的值,并且希望访问仅存在于联合的某些成员上的属性,那个么需要通过类型保护执行某种类型的值。例如,您可以使用中的
运算符作为类型保护(由以下实现):
在您的情况下,这意味着将您的分解分为两种情况:
let startKey: string;
let endKey: string | undefined;
let limit: number | undefined;
if ("endKey" in options) {
const { startKey, endKey } = options;
} else {
const { startKey, limit } = options;
}
(类型保护中的很有用,但在技术上不安全,因为对象类型在TypeScript中是开放的和可扩展的。可以使用foo
属性获取Bar
对象,如下所示:
const hmm = { bar: "hello", foo: 123 };
processFooOrBar(hmm); // no error at compiler time, but impl will error at runtime
所以要小心……但实际上这种情况很少发生)
处理此问题的另一种方法是,在进行分解之前,扩展到具有显式可选属性的类型。您已经将此作为一种解决方法,但不需要触摸选项
类型本身。只需将options
值从options
扩大到类似StartKey&Partial
:
这不同于您的选项
,因为XorOptions
联合的每个成员都明确提到了每个属性。然后,您可以无问题地分解结构:
function retrieve2(options: XorOptions) {
const {
startKey,
endKey,
limit,
} = options;
}
在@jcalz建议的基础上,我定义了KeyBounds,通过引入一个Xor类型,可以交替使用endKey或limit来实现,但不能同时使用两者。这将强制未使用路径(A或B)中的属性名称具有类型从不,而不是没有定义
一旦有了一个定义,即使它有时解析为“永不”,那么分解结构也可以毫无错误地发生
type Xor<A, B> =
| (A & { [k in keyof B]?: never })
| (B & { [k in keyof A]?: never });
interface StartKey {
startKey: string;
}
interface EndKey {
endKey: string;
}
interface Limit {
limit: number;
}
type KeyBounds = StartKey & Xor<EndKey, Limit>;
export type Options = {
someparam:string
} & KeyBounds;
function retrieve(options:Options){
const {
startKey,
endKey,
limit,
} = options;
}
类型Xor=
|(A&{[k in keyof B]?:never})
|(B&{[k in keyof A]?:never});
接口启动键{
startKey:字符串;
}
接口结束键{
endKey:字符串;
}
界面极限{
限制:数量;
}
类型KeyBounds=StartKey&Xor;
导出类型选项={
someparam:string
}&KeyBounds;
函数检索(选项:选项){
常数{
startKey,
结束键,
极限,
}=期权;
}
KeyRange | KeyLimit
仅与一个属性startKey
和type:D[“type”]重叠
只提供了额外的属性type
@Silvermind我所期望的是limit和endKey只是有时存在,而不是它们根本不存在,因此引发编译器错误。我希望联合的交集的行为类似于下面的游乐场,其中endKey和limit都是可选的,但它们的解构(即使未定义)这不是一个错误吗@Silvermind我添加了一个对比的例子来澄清这个问题。然后,也许你需要通过使用部分联合来选择它:type KeyBounds=partial&partial
@Silvermind解决了错误,但引入了一种不幸的情况,即API现在表明endKey和limit一起可能有意义,即使它们没有意义。以前的战略
type XorOptions = {
startKey: string,
endKey?: never,
limit: number,
someParam: string
} | {
startKey: string,
endKey: string,
limit?: never,
someParam: string
}
function retrieve2(options: XorOptions) {
const {
startKey,
endKey,
limit,
} = options;
}
type Xor<A, B> =
| (A & { [k in keyof B]?: never })
| (B & { [k in keyof A]?: never });
interface StartKey {
startKey: string;
}
interface EndKey {
endKey: string;
}
interface Limit {
limit: number;
}
type KeyBounds = StartKey & Xor<EndKey, Limit>;
export type Options = {
someparam:string
} & KeyBounds;
function retrieve(options:Options){
const {
startKey,
endKey,
limit,
} = options;
}