是否可以在 TypeScript 注释中组合多种类型的成员?
Posted
技术标签:
【中文标题】是否可以在 TypeScript 注释中组合多种类型的成员?【英文标题】:Is it possible to combine members of multiple types in a TypeScript annotation? 【发布时间】:2015-02-04 04:36:20 【问题描述】:似乎我想做的事情是不可能的,但我真的希望它是。
本质上,我有两个接口,我想将单个函数参数注释为它们的组合。
interface ClientRequest
userId: number
sessionKey: string
interface Coords
lat: number
long: number
然后,在函数中,我想做这样的事情:
function(data: ClientRequest&Coords) ...
这样我的“数据”对象就可以包含这两种类型的所有成员。
我在 spec preview 的“组合类型的成员”下看到了一些引用,但它似乎还没有出现。
如果不可能,我的解决方案可能如下所示:
interface ClientRequest<T>
userId: number
sessionKey: string
data?: T
function(data: ClientRequest<Coords>) ...
这在这种情况下会起作用,尽管它不像我想要的那样动态。我真的很希望能够在注释本身中组合多种(2+)类型:
function(data: TypeA&TypeB&TypeC) ...
我猜想传统的解决方案是定义一个扩展这些类型的类型,尽管这看起来不太灵活。如果我想添加一个类型,我必须要么
(a) 返回声明并重写它,或者 (b) 创建一个全新的界面。不确定我是否同意额外的开销。有没有 TypeScript 专家愿意为我指明正确的方向?
【问题讨论】:
我试图通过定义interface Both<A,B> extends A, B
来启用此功能,但被告知“一个接口只能扩展一个类或另一个接口”。 Looks like a dead end.
【参考方案1】:
您的问题的具体答案是:不,没有一个内联注释来表示组合或扩展类型。
对于您要解决的问题,最佳做法是创建第三种类型来扩展其他两种类型。
interface IClientRequestAndCoords extends IClientRequest, ICoords
function(data: IClientRequestAndCoords)
更新 2018-10-30
TypeScript 现在有类型交集。所以你现在可以简单地做:
interface ClientRequest
userId: number
sessionKey: string
interface Coords
lat: number
long: number
function log(data: ClientRequest & Coords)
console.log(
data.userId,
data.sessionKey,
data.lat,
data.long
);
【讨论】:
【参考方案2】:如果你使用 ES6 Object.assign
是很有可能的。假设您有这些类型的现有对象。
首先让我们定义类型
interface ClientRequest
userId: number
sessionKey: string
interface Coords
lat: number
long: number
现在两者的结合:
type Combined = ClientRequest & Coords;
假设您有两个想要传递给函数的现有对象:
const foo: ClientRequest =
userId: 10,
sessionKey: "gjk23h872ty3h82g2ghfp928ge"
const bar: Coords =
lat: -23,
long: 52
你可以这样组合它们:
const myData: Combined = Object.assign(, foo, bar);
或者像这样创建一个新的:
const myData: Combined =
userId: 10,
sessionKey: "gjk23h872ty3h82g2ghfp928ge",
lat: -23,
long: 52,
上一个方法
不是类型安全的。
<Type> ...
语法将对象转换为尖括号 (Type
) 中指定的类型,从而绕过 Typescript 的检查器。见Type Assertion。
const myData = Object.assign(,
<ClientRequest>
userId: 10,
sessionKey: "gjk23h872ty3h82g2ghfp928ge"
,
<Coords>
lat: -23,
long: 52
);
最后调用函数:
function myFunc(data: Combined) ...
myFunc(myData);
查看其他问题以了解更多方法:
How can I merge properties of two javascript objects dynamically?
【讨论】:
你也可以将ClientRequest & Coords
内联,像这样:function myFunc(data: ClientRequest & Coords)
第二个代码块到底发生了什么?它看起来像一个类型断言,但在这种情况下不起作用。是不是模板化对象构造的一种形式>
@LouisGarczynski 实际上是Type Assertion。我不推荐它,所以我更新了答案【参考方案3】:
接口答案是组合这两种结构的一种相当优雅的方法,但您提到您想知道是否可以将类型组合为注释的一部分。
关于接口的说明
我已经提供了与您的问题相关的一些功能的一些描述,但首先我要说的是,如果您因为认为您必须创建一个 ICoords
接口(如在您的质疑它看起来更像一个类) - 放轻松 - 因为接口也可以扩展一个类:
// Interface extending an interface and a class
interface IClientRequestAndCoords extends IClientRequest, Coords
界面甚至会合并属性,只要它们具有相同的名称和类型。 (例如,如果他们都声明了一个属性x: string
。
以下是您提到的其他注释功能的注释。
联合类型
你可能读过的规范是联合类型,它看起来像这样:
var x: IClientRequest | Coords;
但这只能确保x
是其中之一,而不是两者的组合。据我所知,您的合并类型 IClientRequest & Coords
的语法不在路线图上。
function go(data: IClientRequest | Coords)
var a = data[0]; // IClientRequest
var b = data[1]; // Coords
// Allowed (even though it doesn't supply Coords data
go(myClientRequest);
// Allowed (even though it doesn't supply IClientRequest data
go (myCoords);
这也不是当前版本的一部分,但稍后会发布。
元组类型
您可能已经看到的规范的另一个可能部分是元组类型:
var x: [IClientRequest, Coords];
但这会将数据的形状从结构更改为数组,其中元素0
是IClientRequest
,元素1
是Coords
。
function go(data: [IClientRequest, Coords])
var a = data[0]; // ClientRequest
var b = data[1]; // Coords
go([myClientRequest, myCoords]);
Uber-注释
最后,如果您真的不想创建合并界面,您可以使用 uber-annotation:
function go(data: userId:number; sessionKey: string; x: number; y: number; )
【讨论】:
这不是提议的联合功能的工作方式。在函数 go(data: IClientRequest | Coords) 的示例中, data 的值将不是数组或元组,而是满足任一接口的对象。然后由函数来解决,即: if (data.hasOwnProperty('userId') // must be IClientRequest @Martin - 我不能不同意,因为我认为这正是我所说的?!But this only ensures that x is either one or the other, not a combination of the two
.
是的。你说的对。我又读了一遍,这就是你所说的 :) 在我之前的阅读中,我把元组上的代码示例误认为是你如何在函数中使用联合的延续。我的错,正如他们在 90 年代所说的那样。【参考方案4】:
我在 spec preview 的“组合类型的成员”下看到了一些引用,但它似乎还没有出现。
我认为您会对intersection
类型(而不是union
)更感兴趣。不同之处在于传入的对象必须支持所有属性,而不是其中一个。
Github 问题:https://github.com/Microsoft/TypeScript/issues/1256#issuecomment-64533287
【讨论】:
是的。这正是我所希望的。【参考方案5】:我只需要这样做,以下解决方案对我有用:
type MyFunction = () => void
interface MyFunctionWithProps extends MyFunction
prop1: string,
prop2: number
// compiles fine
const MyMashup: MyFunctionWithProps = Object.assign(
() => ,
prop1: 'hello',
prop2: 1234
)
【讨论】:
【参考方案6】:现在,如果类型 P1 和 P2 扩展对象,您可以使用条件类型执行类似的操作:
type P1UnionP2 = [k in (keyof P1 | keyof P2)]: k extends keyof P1 ? P1[k] : k extends keyof P2 ? P2[k] : never
如果这适用于您的情况,最好的方法是:
interface P1UnionP2 extends P1, P2
【讨论】:
以上是关于是否可以在 TypeScript 注释中组合多种类型的成员?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用严格类型的有效负载发出事件? | Vue 3 组合 API + TypeScript
java查找string1和string2是不是含有相同的字母种类和数量(string1是否是string2的重新组合)