是否可以在TypeScript注释中组合多种类型的成员?

时间:2014-12-05 22:13:45

标签: javascript typescript interface

似乎我想做的事情是不可能的,但我真的希望它是。

基本上,我有两个接口,我想将一个函数参数注释为两者的组合。

interface ClientRequest {
    userId:     number
    sessionKey: string
}

interface Coords {
    lat:  number
    long: number
}

然后,在函数中,我想做这样的事情:

function(data: ClientRequest&Coords) { ... }

这样我的数据' object可以包含两种类型的所有成员。

我在spec preview,"结合类型'中看到了引用的内容。会员",但似乎还没有成功。

如果不可能,我的解决方案可能如下所示:

interface ClientRequest<T> {
    userId:     number
    sessionKey: string
    data?:       T
}

function(data: ClientRequest<Coords>) { ... }

在这种情况下会有效,尽管它不像我想的那样动态。我真的希望能够在注释本身中组合多种(2 +)类型:

function(data: TypeA&TypeB&TypeC) { ... }

我猜想传统的解决方案是定义一种扩展这些类型的类型,尽管这似乎不那么灵活。如果我想添加一个类型,我必须

  • (a)返回声明并重写,或
  • (b)创建一个全新的界面。不确定我同意额外的开销。

任何TypeScript专家都指出我的方向正确吗?

6 个答案:

答案 0 :(得分:30)

您的问题的具体答案是:不,没有一个内联注释来表示组合或扩展类型。

您尝试解决的问题的最佳做法是创建第三种类型,以扩展其他两种类型。

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
  );
}

答案 1 :(得分: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];

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

function go(data: [IClientRequest, Coords]) {
    var a = data[0]; // ClientRequest
    var b = data[1]; // Coords
}

go([myClientRequest, myCoords]);

乌伯-注释

最后,如果你真的不想创建一个合并的界面,你可以使用超级注释:

function go(data: { userId:number; sessionKey: string; x: number; y: number; } ) {

}

答案 2 :(得分:2)

  

我在spec preview,&#34;结合类型&#39;中看到了引用的内容。会员&#34;,但似乎还没有成功。

我认为您对intersection类型(不是union)更感兴趣。不同之处在于传入的对象必须支持所有属性,而不是 之一。

Github问题:https://github.com/Microsoft/TypeScript/issues/1256#issuecomment-64533287

答案 3 :(得分:2)

如果您使用 ES6 Object.assign

,则非常有可能
interface ClientRequest {
    userId:     number
    sessionKey: string
}

interface Coords {
    lat:  number
    long: number
}

type Combined = ClientRequest & Coords;

然后您可以像这样定义数据:

let 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?

答案 4 :(得分:0)

我只需要这样做,以下解决方案对我有用:

type MyFunction = () => void

interface MyFunctionWithProps extends MyFunction {
  prop1: string,
  prop2: number
}

// compiles fine
const MyMashup: MyFunctionWithProps = Object.assign(
  () => {},
  {
    prop1: 'hello',
    prop2: 1234
  }
)

答案 5 :(得分:0)

现在,如果类型 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 { }