简体   繁体   English

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

[英]Is it possible to combine members of multiple types in a TypeScript annotation?

It seems that what I am trying to do is not possible, but I really hope it is.似乎我想做的事情是不可能的,但我真的希望它是。

Essentially, I have two interfaces, and I want to annotate a single function parameter as the combination of both of them.本质上,我有两个接口,我想将单个函数参数注释为它们的组合。

interface ClientRequest {
    userId:     number
    sessionKey: string
}

interface Coords {
    lat:  number
    long: number
}

And then, in the function, I want to do something like this:然后,在函数中,我想做这样的事情:

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

So that my 'data' object could contain all of the members from both types.这样我的“数据”对象就可以包含这两种类型的所有成员。

I saw something referenced in a spec preview , under "Combining Types' Members", but it seems like this hasn't made it in yet.我在“组合类型的成员”下的规范预览中看到了一些引用,但似乎这还没有实现。

If it isn't possible, my solution might look like this:如果不可能,我的解决方案可能如下所示:

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

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

Which would work in this case, although it's not as dynamic as I would like.这在这种情况下会起作用,尽管它不像我想要的那样动态。 I would really like to be able to combine multiple (2+) types in the annotation itself:我真的希望能够在注释本身中组合多种(2+)类型:

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

I would guess that the conventional solution is to define a type that extends those types, although that seems less flexible.我猜想传统的解决方案是定义一个扩展这些类型的类型,尽管这似乎不太灵活。 If I want to add a type, I would have to either如果我想添加一个类型,我必须要么

  • (a) go back to the declaration and rewrite it, or (a) 回到声明并重写它,或
  • (b) create an entirely new interface. (b) 创建一个全新的界面。 Not sure I agree with the extra overhead.不确定我是否同意额外的开销。

Any TypeScript experts care to point me in the right direction?有没有 TypeScript 专家愿意为我指明正确的方向?

The specific answer to your question is: no, there is not a single inline annotation to signify combined or extended types.您的问题的具体答案是:不,没有一个内联注释来表示组合或扩展类型。

The best practice for the problem you are trying to solve would be to create third type that would extend the other two.您尝试解决的问题的最佳实践是创建第三种类型来扩展其他两种类型。

interface IClientRequestAndCoords extends IClientRequest, ICoords {} 

function(data: IClientRequestAndCoords) 

UPDATE 2018-10-30更新2018-10-30

TypeScript now has type intersections. TypeScript 现在有类型交集。 So you can now simply do:所以你现在可以简单地做:

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

It's very possible if you use ES6 Object.assign .如果你使用ES6 Object.assign是很有可能的。 Assuming you have existing objects of those types.假设您有这些类型的现有对象。

First let's define the types首先让我们定义类型

interface ClientRequest {
    userId:     number
    sessionKey: string
}

interface Coords {
    lat:  number
    long: number
}

Now the combination of both:现在两者的结合:

type Combined = ClientRequest & Coords;

Assuming you have two existing objects which you would like to pass to the function:假设您有两个想要传递给函数的现有对象:

const foo: ClientRequest = {
    userId: 10,
    sessionKey: "gjk23h872ty3h82g2ghfp928ge"
}

const bar: Coords = {
    lat: -23,
    long: 52
}

You can combine them like this:你可以像这样组合它们:

const myData: Combined = Object.assign({}, foo, bar);

Or simply create a new one like this:或者像这样创建一个新的:

const myData: Combined = {
    userId: 10,
    sessionKey: "gjk23h872ty3h82g2ghfp928ge",
    lat: -23,
    long: 52,
}

Previous method以前的方法

Not type-safe.不是类型安全的。

The <Type> {...} syntax casts the object to the type specified in the angle brackets ( Type ), which bypasses Typescript's checker. <Type> {...}语法将对象转换为尖括号 ( Type ) 中指定的类型,这绕过了 Typescript 的检查器。 See Type Assertion .请参阅类型断言

const myData = Object.assign({},
    <ClientRequest> {
        userId: 10,
        sessionKey: "gjk23h872ty3h82g2ghfp928ge"
    },
    <Coords> {
        lat: -23,
        long: 52
    }
);

Finally, call the function:最后,调用函数:

function myFunc(data: Combined) { ... }
myFunc(myData);

See this other question for even more ways of accomplishing this:有关完成此操作的更多方法,请参见其他问题:

How can I merge properties of two JavaScript objects dynamically? 如何动态合并两个 JavaScript 对象的属性?

The interface answer is a reasonably graceful method of combining the two structures, but you mention that you want to know if it is possible to combine the type as part of an annotation.接口答案是组合这两种结构的一种相当优雅的方法,但您提到您想知道是否可以将类型组合为注释的一部分。

A note on interfaces关于接口的说明

I have supplied some descriptions of a few features related to your question, but first I would say that if you are put off of the interface solution because you think you'll have to create an ICoords interface (as in your question it looks more like a class) - rest easy - because an interface can extend a class too:我已经提供了与您的问题相关的一些功能的一些描述,但首先我要说的是,如果您因为认为必须创建一个ICoords界面而被推迟使用界面解决方案(因为在您的问题中它看起来更像一个类) - 休息一下 - 因为接口也可以扩展一个类:

// Interface extending an interface and a class
interface IClientRequestAndCoords extends IClientRequest, Coords {} 

The interface will even merge properties as long as they have the same name and type.只要属性具有相同的名称和类型,该接口甚至会合并属性。 (For example if they both declared a property x: string . (例如,如果他们都声明了一个属性x: string

Here are notes on the other annotation features you allude to.以下是您提到的其他注释功能的注释。

Union Types联合类型

The specification you may have read is the union type, which looks like this:你可能读过的规范是联合类型,它看起来像这样:

var x: IClientRequest | Coords;

But this only ensures that x is either one or the other, not a combination of the two.但这只能确保x是其中之一,而不是两者的组合。 Your syntax of a merged type IClientRequest & Coords isn't on the roadmap as far as I know.据我所知,您的合并类型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);

This also isn't part of the current release, but is coming later.这也不是当前版本的一部分,但稍后会发布。

Tuple Types元组类型

Another possible part of the specification you may have seen is tuple types:您可能已经看到的规范的另一个可能部分是元组类型:

var x: [IClientRequest, Coords];

But this would change the shape of the data from being a structure to being like an array where element 0 is an IClientRequest and element 1 is an Coords .但这会将数据的形状从结构更改为数组,其中元素0IClientRequest元素1Coords

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

go([myClientRequest, myCoords]);

Uber-Annotation Uber-注解

And finally, if you really don't want to create a merged interface, you could just use an uber-annotation:最后,如果你真的不想创建一个合并的界面,你可以使用一个 uber-annotation:

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

}

You can do something like this, now, with conditional types, if types P1 and P2 extends of object:现在,如果类型 P1 和 P2 扩展对象,您可以使用条件类型执行类似的操作:

type P1UnionP2 = { [k in (keyof P1 | keyof P2)]: k extends keyof P1 ? P1[k] : k extends keyof P2 ? P2[k] : never }

The best approach is, if that's applies in your case, this:最好的方法是,如果这适用于您的情况,则:

interface P1UnionP2 extends P1, P2 { }

I saw something referenced in a spec preview , under "Combining Types' Members", but it seems like this hasn't made it in yet.我在“组合类型的成员”下的规范预览中看到了一些引用,但似乎这还没有实现。

I think you would be more interested in intersection types (not union ).我认为您会对intersection类型(而不是union )更感兴趣。 The difference is that the object passed in must support all of the properties and not one of .不同之处在于传入的对象必须支持所有属性,而不是.

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

This is a reusable utility type based on this answer , for combining a union of Record types这是基于此答案的可重用实用程序类型,用于组合Record类型的联合

type UnionToType<U extends Record<string, unknown>> = { [K in (U extends unknown ? keyof U : never)]: U extends unknown ? K extends keyof U ? U[K] : never : never}

type A = {
    a: string
}

type B = {
    b: number
}

type C = {
    c: boolean
}

type D = {
    c: A
}

type Combined = UnionToType<A | B | C | D>

/*
type Combined = {
    a: string;
    b: number;
    c: boolean | A;
}
*/

const combined1: Combined = {
    a: 'test',
    b: 2,
    c: {
        a: ''
    }
}

const combined2: Combined = {
    a: 'test',
    b: 2,
    c: false
}

Playground example游乐场示例

I just needed to do this and the following solution worked for me:我只需要这样做,以下解决方案对我有用:

type MyFunction = () => void

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM