TypeScript:将元组类型转换为对象 [英] TypeScript: convert tuple type to object

查看:18
本文介绍了TypeScript:将元组类型转换为对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

总结:我有一个这样的元组类型:

Summary: I have a tuple type like this:

[session: SessionAgent, streamID: string, isScreenShare: boolean, connectionID: string, videoProducerOptions: ProducerOptions | null, connection: AbstractConnectionAgent, appData: string]

我想把它转换成这样的对象类型:

and I want to convert it to an object type like this:

type StreamAgentParameters = {
  session: SessionAgent
  streamID: string
  isScreenShare: boolean
  connectionID: string
  videoProducerOptions: ProducerOptions | null
  connection: AbstractConnectionAgent
  appData: string
}

有没有办法做到这一点?

Is there a way to do that?

我想创建一个工厂函数用于测试类以简化设置.

I want to create a factory function for tests for a class to simplify the setup.

export type Factory<Shape> = (state?: Partial<Shape>) => Shape

我想避免手动输入类的参数,因此我寻找获取构造函数参数的可能性.你知道吗,有 ConstructorParameters 辅助类型.不幸的是,它返回的是一个元组而不是一个对象.

I want to avoid manually typing out the parameters for the class, so I looked for possibilities to get the parameters for the constructor. And what do you know, there is the ConstructorParameters helper type. Unfortunately, it returns a tuple instead of an object.

因此以下内容不起作用,因为元组不是对象.

Therefore the following doesn't work because a tuple is NOT an object.

type MyClassParameters = ConstructorParameters<typeof MyClass>
// ↵ [session: SessionAgent, streamID: string, isScreenShare: boolean, connectionID: string, videoProducerOptions: ProducerOptions | null, connection: AbstractConnectionAgent, appData: string]

const createMyClassParameters: Factory<MyClassParameters> = ({
  session = new SessionAgent(randomRealisticSessionID()),
  streamID = randomRealisticStreamID(),
  isScreenShare = false,
  connectionID = randomRealisticConnectionID(),
  videoProducerOptions = createPopulatedProducerOptions(),
  connection = new ConnectionAgent(
    new MockWebSocketConnection(),
    'IP',
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
  ),
  appData = 'test',
} = {}) => ({
  session,
  streamID,
  isScreenShare,
  connectionID,
  videoProducerOptions,
  connection,
  appData,
})

我尝试创建一个将元组转换为对象的辅助类型,但我最好的尝试是这样(但没有成功).

I tried creating a helper type that converts a tuple to an object, but my best attempt was this (and it didn't work).

type TupleToObject<T extends any[]> = {
  [key in T[0]]: Extract<T, [key, any]>[1]
}

我该如何解决这个问题?

How can I solve this problem?

推荐答案

正如其他答案中提到的,没有办法转换元组 labels 到字符串 文字类型;标签仅用于文档,不影响类型系统:类型 [foo: string][bar: string][string] 都是等价的.所以任何将 [foo: string] 变成 {foo: string} 的方法也应该将 [bar: string] 变成 {foo: 字符串}.所以我们需要放弃捕获元组标签.

As mentioned in the other answers, there's no way to convert tuple labels into string literal types; the labels are just for documentation and don't affect the type system: the types [foo: string] and [bar: string] and [string] are all equivalent to each other. So any method to turn [foo: string] into {foo: string} should also turn [bar: string] into {foo: string}. So we need to give up on capturing tuple labels.

元组的真正键是数字字符串,如 0"1".如果你只是想把一个元组变成一个类似的类型,只用那些类似数字的键而不是所有的数组属性和方法,你可以这样做:

The real keys of a tuple are numeric strings like "0" and 1". If you just want to turn a tuple into a similar type with just those numeric-like keys and not all the array properties and methods, you can do it like this:

type TupleToObject<T extends any[]> = Omit<T, keyof any[]>

这只是使用 Omit 实用程序类型 忽略所有数组中存在的任何元组属性(如 lengthpush 等).这也或多或少等同于

This just uses the Omit<T, K> utility type to ignore any tuple properties that exist in all arrays (like length, push, etc). This is also more or less equivalent to

type TupleToObject<T extends any[]> = 
  { [K in keyof T as Exclude<K, keyof any[]>]: T[K] }

使用 映射类型显式过滤掉键.

这是它在您的元组类型上的行为方式:

Here's how it behaves on your tuple type:

type StreamAgentObjectWithNumericlikeKeys = TupleToObject<StreamAgentParameters>
/* type StreamAgentObjectWithNumericlikeKeys = {
    0: SessionAgent;
    1: string;
    2: boolean;
    3: string;
    4: ProducerOptions | null;
    5: AbstractConnectionAgent;
    6: string;
} */

你也可以创建一个函数来对实际值做同样的事情:

You could also make a function to do the same thing to actual values:

const tupleToObject = <T extends any[]>(
  t: [...T]) => ({ ...t } as { [K in keyof T as Exclude<K, keyof any[]>]: T[K] });
const obj = tupleToObject(["a", 2, true]);
/* const obj: {
    0: string;
    1: number;
    2: boolean;
} */
console.log(obj) // {0: "a", 1: 2, 2: true};


如果您愿意在类型元组之外保留属性 names 的元组,则可以编写一个函数,将数字元组键映射到相应的名称:


If you are willing to hold onto an tuple of property names in addition to your tuple of types, you can write a function which maps the numeric tuple keys to the corresponding name:

type TupleToObjectWithPropNames<
  T extends any[],
  N extends Record<keyof TupleToObject<T>, PropertyKey>
  > =
  { [K in keyof TupleToObject<T> as N[K]]: T[K] };
   
type StreamAgentParameterNames = [
  "session", "streamID", "isScreenShare", "connectionID",
  "videoProducerOptions", "connection", "appData"
];

type StreamAgentObject =
  TupleToObjectWithPropNames<StreamAgentParameters, StreamAgentParameterNames>
/* 
type StreamAgentObject = {
  session: SessionAgent
  streamID: string
  isScreenShare: boolean
  connectionID: string
  videoProducerOptions: ProducerOptions | null
  connection: AbstractConnectionAgent
  appData: string
}
*/

你可以创建一个函数来对实际值做同样的事情:

And you can make a function to do the same to actual values:

const tupleToObjectWithPropNames = <T extends any[],
  N extends PropertyKey[] & Record<keyof TupleToObject<T>, PropertyKey>>(
    tuple: [...T], names: [...N]
  ) => Object.fromEntries(Array.from(tuple.entries()).map(([k, v]) => [(names as any)[k], v])) as
  { [K in keyof TupleToObject<T> as N[K]]: T[K] };

const objWithPropNames = tupleToObjectWithPropNames(["a", 2, true], ["str", "num", "boo"])
/* const objWithPropNames: {
    str: string;
    num: number;
    boo: boolean;
} */
console.log(objWithPropNames); // {str: "a", num: 2, boo: true}

游乐场链接到代码

这篇关于TypeScript:将元组类型转换为对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆