TypeScript:将元组类型转换为对象 [英] TypeScript: convert tuple type to object
问题描述
总结:我有一个这样的元组类型:
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
实用程序类型 忽略所有数组中存在的任何元组属性(如 length
、push
等).这也或多或少等同于
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屋!