typescript - 防止函数调用中的类型冲突
问题描述
我有很多这样的接口:
interface A {
type: 'A';
data: {
a: string;
};
}
interface B {
type: 'B';
data: {
b: string;
};
}
我有一种将它们结合在一起的类型:
type Item = A | B;
我想创建一个函数,将新项目推送到定义为const items: Item[] = [];
.
到目前为止,我有这个:
const create = <I extends Item>(type: I['type'], data: I['data']) => items.push({type, data});
但是,我希望这会导致类型错误,type
并且data
具有冲突的类型,但它不会:
create('A', {b: '1'});
如何定义不允许create
冲突type
和值?data
请注意,在我的真实代码中,我的接口中的字段不仅仅是由我type
的函数data
生成的字段create
,因此我不能简单地将 anItem
作为参数。
预先感谢您的帮助!
解决方案
这里的问题是在调用. 您的调用签名要求编译器推断类型和的给定值,而编译器只是没有能力做到这一点。在某一时刻,在microsoft/TypeScript#20126中进行了一些工作以启用此功能,但由于某种原因从未合并。由于没有推断,编译器放弃并扩大到它的约束,即:I
create()
I
I['type']
I['data']
I
I
Item
create('A', { b: '1' });
/* const create: <Item>(type: "A" | "B", data: {
a: string;
} | {
b: string;
}) => number */
并且没有错误,因为"A"
肯定可以分配给Item["type"]
,并且{b: '1'}
肯定可以分配给Item["data"]
。这不是你想要的,所以这个调用签名对你不起作用。
编译器最擅长X
从 type 的值X
(而不是type 的值X['someProperty']
)推断类型参数。因此,让我们这样做并从中计算您需要的类型:
const create = <K extends Item["type"]>(
type: K, data: Extract<Item, { type: K }>['data']
) => items.push({ type, data } as Item);
在这里,我们从 推断K
出type
一个 type 的值K
。这被限制为Item["type"]
。然后我们计算 的必要类型data
,方法是获取属性为 typeExtract
的联合成员,然后查看其属性。这可以按需要工作:type
K
data
create("A", { a: "1" }); // okay
create("B", { b: "1" }); // okay
create("A", { b: "1" }); // error!
// ---------> ~~~~~~
// Argument of type '{ b: string; }' is not assignable to
// parameter of type '{ a: string; }'
这里的另一种方法是放弃泛型,只让函数接受一个休息参数,其类型是从 派生的休息元组的联合:Item
type CreateArgs = Item extends infer I ? I extends Item ?
[type: I['type'], data: I['data']] : never : never;
/* type CreateArgs = [type: "A", data: {
a: string;
}] | [type: "B", data: {
b: string;
}] */
在这里,我使用条件类型在联合上分配参数列表操作Item
。现在create()
可能看起来像这样:
const create = (...[type, data]: CreateArgs) =>
items.push({ type, data } as Item);
它的行为与以前相似。从调用者的角度来看,它就像create()
一个重载函数:
create("A", { a: "1" }); // okay
create("B", { b: "1" }); // okay
create("A", { b: "1" }); // error!
// ---------> ~~~~~~
// Type '{ b: string; }' is not assignable to parameter of type '{ a: string; }'
无论哪种方式都应该适合你。
推荐阅读
- python - 更新滑块值不断使程序崩溃
- azure - 多因素身份验证注册重定向
- regex - 我们可以使用基于lookbehinds的条件吗?
- reactjs - 如何在 React 应用程序中使用 axios 响应解决 TypeScript 错误
- python - 如何在 Python lambda 中安装 TensorFlow Serving Client API?
- python - 使用脚本迁移 Odoo 模块
- r - 在 R Shiny Dashboard 的反应函数中返回数据框
- git - 似乎无法从本地 GIT 存储库中删除
- android - Litho ListRecyclerConfiguration Kotlin 与 linearLayoutInfoFactory
- android - NestedScrollView + CoodinatorLayout scrollBy() scrollTo() 方法什么都不做