javascript 是否可以在TypeScript annotation中合并多个类型的成员?

xytpbqjk  于 2023-05-21  发布在  Java
关注(0)|答案(7)|浏览(95)

看起来我正在努力做的事情是不可能的,但我真的希望它是。
本质上,我有两个接口,我想将一个函数参数注解为这两个接口的组合。

interface ClientRequest {
    userId:     number
    sessionKey: string
}

interface Coords {
    lat:  number
    long: number
}

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

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

这样我的'data'对象就可以包含这两种类型的所有成员。
我在spec preview中看到了一些引用,在“组合类型的成员”下,但似乎还没有实现。
如果不可能,我的解决方案可能是这样的:

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

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

这在这种情况下是可行的,尽管它不像我希望的那样动态。我真的希望能够在注解本身中合并多个(2+)类型:

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

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

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

有没有TypeScriptMaven愿意为我指出正确的方向?

ni65a41a

ni65a41a1#

回复更新日期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
  );
}

原始答案

对你的问题的具体回答是:不,没有一个内联注解来表示组合或扩展类型。
对于您试图解决的问题,最好的做法是创建第三种类型,以扩展其他两种类型。

interface IClientRequestAndCoords extends IClientRequest, ICoords {} 

function(data: IClientRequestAndCoords)
jtjikinw

jtjikinw2#

如果你使用ES6Object.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的检查器。见类型Assert。*
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?

q9yhzks0

q9yhzks03#

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

接口说明

我已经提供了一些与您的问题相关的功能的描述,但首先我要说的是,如果您因为认为必须创建ICoords接口(在您的问题中,它看起来更像是一个类)而推迟了接口解决方案-请放心-因为接口也可以扩展类:

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

只要属性具有相同的名称和类型,接口甚至会合并它们。(例如,如果它们都声明了属性x: string
下面是您提到的其他注解特性的注解。

联合类型

你可能读过的规范是union类型,它看起来像这样:

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-Annotation

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

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

}
2fjabf4q

2fjabf4q4#

这是一个基于this answer的可重用实用程序类型,用于组合Record类型的联合

type MergeUnionOfRecordTypes<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 = MergeUnionOfRecordTypes<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

iqjalb3h

iqjalb3h5#

现在,如果类型P1和P2扩展了object,你可以用条件类型做这样的事情:

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 { }
7gcisfzg

7gcisfzg6#

我在spec preview中看到了一些引用,在“组合类型的成员”下,但似乎还没有实现。
我认为你会对intersection类型更感兴趣(而不是union)。不同之处在于传入的对象必须支持 * 所有 * 属性,而不是 * 其中之一 *。
Github问题:https://github.com/Microsoft/TypeScript/issues/1256#issuecomment-64533287

vsikbqxv

vsikbqxv7#

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

type MyFunction = () => void

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

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

相关问题