是否可以在 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&lt;A,B&gt; 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,

上一个方法

不是类型安全的。

&lt;Type&gt; ... 语法将对象转换为尖括号 (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 &amp; Coords 内联,像这样:function myFunc(data: ClientRequest &amp; 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 &amp; 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];

但这会将数据的形状从结构更改为数组,其中元素0IClientRequest,元素1Coords

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的重新组合)

jspHtml页面注释的种类

是否可以在 Typescript 中装饰装饰器?

使用 Typescript 在 Vue3 组合 API 中观察 Value Prop

UML中几种类间关系:继承实现依赖关联聚合组合的联系与区别