[英]How to define an object type that does _not_ include a specific member
I am working with objects I put in and out of databases/actions and sometimes wish to add and remove database/action-specific fields. 我正在处理我放入和退出数据库/动作的对象,有时希望添加和删除特定于数据库/动作的字段。
For example, I have: 例如,我有:
type EntryEvent<EntryLine> = { type: 'entry' } & EntryLine;
Elsewhere I wish to drop the type
member in order to turn an EntryEvent<EntryLine>
back into an EntryLine
. 在其他地方,我希望删除
type
成员,以便将EntryEvent<EntryLine>
回EntryLine
。 To do that I use a utility function as follows: 为此,我使用以下实用程序函数:
export function stripFields<T extends object, K extends keyof T>(
o: T,
fields: K[]
): Omit<T, K> {
const result: Partial<T> = { ...(<object>o) };
for (const field of fields) {
delete result[field];
}
return <Omit<T, K>>result;
}
The trouble is, the resulting object won't be recognized as an EntryLine
. 麻烦的是,生成的对象将不会被识别为
EntryLine
。
If I try to assign the following to an EntryLine
type: 如果我尝试将以下内容分配给
EntryLine
类型:
stripFields(event, ['type'])
I will get: 我会得到:
Argument of type 'Pick<EntryEvent<EntryLine>, Exclude<keyof EntryLine, "type">>' is not assignable to parameter of type 'EntryLine'.
'Pick<EntryEvent<EntryLine>, Exclude<keyof EntryLine, "type">>' is assignable to the constraint of type 'EntryLine', but 'EntryLine' could be instantiated with a different subtype of constraint 'Pick<object, never>' ts(2345)
As far as I can tell that makes sense, because if the generic type EntryLine
included a type
member, the result of stripFields
would no longer be an EntryLine
(since it would be missing its type
member). 据我所知,这是有道理的,因为如果通用类型
EntryLine
包含type
成员,则stripFields
的结果将不再是EntryLine
(因为它将丢失其type
成员)。
Is there some way I can define constrain the generic type EntryLine
so that it does not allow objects with a type
field? 有什么方法可以定义约束通用类型
EntryLine
以便它不允许带有type
字段的对象? Or is there some other means of working around this? 还是有其他解决方法?
Update: 更新:
The following code reproduces the problem for me: 以下代码为我重现了该问题:
type EntryEvent<EntryLine> = { type: "entry" } & EntryLine;
type MyEntryLine = {
a: Date;
};
export function stripFields<T extends object, K extends keyof T>(
o: T,
fields: K[]
): Omit<T, K> {
const result: Partial<T> = { ...(<object>o) };
for (const field of fields) {
delete result[field];
}
return <Omit<T, K>>result;
}
type Callback<EntryLine> = (a: EntryLine) => void;
function thingThatTakesAnEvent<EntryLine>(a: EntryEvent<EntryLine>, callback: Callback<EntryLine>) {
callback(stripFields(a, ["type"]));
}
function thingThatTakesAMyEntryLine(a: MyEntryLine) {
console.log(a.a);
}
declare let event: EntryEvent<MyEntryLine>;
thingThatTakesAnEvent(event, thingThatTakesAMyEntryLine);
'Pick< EntryEvent< EntryLine>, Exclude< keyof EntryLine, "type">>' is assignable to the constraint of type 'EntryLine', but 'EntryLine' could be instantiated with a different subtype of constraint '{}'.
“ Pick <EntryEvent <EntryLine>,Exclude <EntryLine的键,“类型” >>”可分配给类型“ EntryLine”的约束,但是可以使用约束“ {}”的另一个子类型实例化“ EntryLine”。
Error messages in this form basically mean, that you tried to infer too much from the passed in generic type parameter, whose exact type only the caller/client can know. 这种形式的错误消息基本上意味着,您试图从传入的泛型类型参数中推断出太多信息,而泛型类型参数只有调用者/客户端才能知道。 Typescript does not consider your speculations about its type to be safe anymore.
Typescript不再认为您对它的类型的猜测是安全的。
Concerning your code, it could be translated to this: 关于您的代码,可以将其翻译为:
function thingThatTakesAnEvent<EntryLine>(
a: EntryEvent<EntryLine>,
callback: Callback<EntryLine>
) {
callback(stripFields(a, ["type"]));
//------ error ------//
}
==> the return type of stripFields
, which is Pick< EntryEvent< EntryLine>, Exclude< keyof EntryLine, "type">>
, is assignable to the constraint EntryLine
(which defaults here to {}
), but what if the caller invoked the function like this: thingThatTakesAnEvent<{foo: "bar"}>
? ==>
stripFields
的返回类型为Pick< EntryEvent< EntryLine>, Exclude< keyof EntryLine, "type">>
,可以分配给约束EntryLine
(默认为{}
),但是如果调用者调用了该方法像这样的函数: thingThatTakesAnEvent<{foo: "bar"}>
? Inside the function we cannot know that it's {foo: "bar"}
, and we should invoke callback
with EntryLine
, but we did with a specific subtype, which may be not the right one from the caller. 在函数内部,我们不知道它是
{foo: "bar"}
,我们应该使用EntryLine
调用callback
,但是我们使用了特定的子类型,这可能与调用者不EntryLine
。
Based on the purpose, you could do the following: 根据目的,您可以执行以下操作:
stripFields
Playground : stripFields
Playground的类型: type Callback<EntryLine> = (a: Omit<EntryEvent<EntryLine>, "type">) => void;
thingThatTakesAndEvent
Playground : thingThatTakesAndEvent
Playground中进行转换 : function thingThatTakesAnEvent<EntryLine>(
a: EntryEvent<EntryLine>,
callback: Callback<EntryLine>
) {
callback((stripFields(a, ["type"]) as unknown) as EntryLine);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.