繁体   English   中英

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

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

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

本质上,我有两个接口,我想将单个函数参数注释为它们的组合。

interface ClientRequest {
    userId:     number
    sessionKey: string
}

interface Coords {
    lat:  number
    long: number
}

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

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

这样我的“数据”对象就可以包含这两种类型的所有成员。

我在“组合类型的成员”下的规范预览中看到了一些引用,但似乎这还没有实现。

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

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

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

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

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

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

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

有没有 TypeScript 专家愿意为我指明正确的方向?

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

您尝试解决的问题的最佳实践是创建第三种类型来扩展其他两种类型。

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

如果你使用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 的检查器。 请参阅类型断言

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

最后,调用函数:

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

有关完成此操作的更多方法,请参见其他问题:

如何动态合并两个 JavaScript 对象的属性?

接口答案是组合这两种结构的一种相当优雅的方法,但您提到您想知道是否可以将类型组合为注释的一部分。

关于接口的说明

我已经提供了与您的问题相关的一些功能的一些描述,但首先我要说的是,如果您因为认为必须创建一个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]);

Uber-注解

最后,如果你真的不想创建一个合并的界面,你可以使用一个 uber-annotation:

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

}

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

我在“组合类型的成员”下的规范预览中看到了一些引用,但似乎这还没有实现。

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

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

这是基于此答案的可重用实用程序类型,用于组合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
}

游乐场示例

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

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