提问人:GuillaumeNo 提问时间:11/1/2023 更新时间:11/1/2023 访问量:39
两个相同的 TypeScript 接口具有不同的名称 - 如何使其干燥/避免重复?
Two identical TypeScript interfaces with different names - how to make it DRY / avoid repetition?
问:
我正在开发一个金融应用程序,它有“出价”(买东西的提议)和“卖出价”(卖东西的提议)。
interface Ask {
price: number
amount: number
}
interface Bid {
price: number
amount: number
}
如何避免重复两个相同的类型定义?(它们将始终相同)
我只能有一个接口并调用它,但它失去了意义和清晰度。Offer
我试过这个:
interface Offer {
price: number
amount: number
}
interface Bid extends Offer {}
interface Ask extends Offer {}
但是我收到一个 eslint 错误,如果我不向它添加一些东西,扩展界面就没有意义了。An interface declaring no members is equivalent to its supertype.
答:
您可以使用类型别名:
interface Ask {
price: number;
amount: number;
}
type Bid = Ask;
请注意,由于 TypeScript 的类型系统是结构化的(基于类型的形状),而不是名词(基于类型的名称/标识),因此具有该类型的对象将可以分配给变量/属性/参数,反之亦然。TypeScript 不区分它们:Ask
Bid
let a: Ask = { price: 42, amount: 2 };
// ^? −−− let a: Ask
let b: Bid = { price: 67, amount: 4 };
// ^? −−− let b: Ask
a = b; // <== This is allowed
请注意,显示的类型提示是 ,而不是 。b
Ask
Bid
如果你想保持这两种类型之间的区别,你可以使用 Drew Colthorp 在 Flavoring: Flexible Nominal Typing for TypeScript 一文中描述的技术(感谢 VLAZ 指出这一点;另请参阅 VLAZ 对相关问题的回答)。它的核心是,你可以通过使用可选的品牌属性来区分类型,如下所示(但请继续阅读):
interface Offer {
price: number;
amount: number;
}
interface Bid extends Offer {
__type__?: "Bid";
}
interface Ask extends Offer {
__type__?: "Ask";
}
由于该属性是可选的,因此在创建实例时不必指定它,但它会使类型彼此不兼容(尽管您可以轻松地从一个类型转换为另一个类型):
let a: Ask = { price: 42, amount: 2 };
// ^? −−− let a: Ask
let b: Bid = { price: 67, amount: 4 };
// ^? −−− let b: Bid
a = b; // <== Error: Type 'Bid' is not assignable to type 'Ask'.
// Types of property '__type__' are incompatible.
// Type '"Bid" | undefined' is not assignable to type '"Ask" | undefined'.(2322)
在本文中,Colthorp 通过创建一个可重用的泛型类型来进一步发展:Flavor
interface Flavoring<FlavorT> {
_type?: FlavorT;
}
type Flavor<T, FlavorT> = T & Flavoring<FlavorT>;
您将在代码中使用,如下所示:
interface Offer {
price: number;
amount: number;
}
type Bid = Flavor<Offer, "Bid">;
type Ask = Flavor<Offer, "Ask">;
与 和 相同的测试表明,您不能将一个分配给另一个。a
b
最后,您可以通过在实例中实际包含 (or ) 属性而不是使它们成为可选属性来使用完全品牌类型。我发现有时这样做很方便,因为我可以在调试器中看到这些属性,并且可以在处理实例的代码中使用它们来缩小类型。但有时你不希望额外的属性使对象混乱,因此采用了上面的“风味”方法。完整品牌示例:_type
__type__
Offer
interface Offer {
price: number;
amount: number;
}
interface Bid extends Offer {
__type__: "Bid";
}
interface Ask extends Offer {
__type__: "Ask";
}
let a: Ask = { price: 42, amount: 2, __type__: "Ask" };
// ^? −−− let a: Ask
let b: Bid = { price: 67, amount: 4, __type__: "Bid" };
// ^? −−− let b: Bid
a = b; // <== Error: Type 'Bid' is not assignable to type 'Ask'.
// Types of property '__type__' are incompatible.
// Type '"Bid"' is not assignable to type '"Ask"'.(2322)
评论
let a1: Ask = { price: 42, amount: 2};
评论
Offer
Offer
Bid
Ask
Offer
Offer
Bid
Ask
Transaction