J. *_*ers 5 parameters constructor tuples object typescript
Summary: I have a tuple type like this:
\n[session: SessionAgent, streamID: string, isScreenShare: boolean, connectionID: string, videoProducerOptions: ProducerOptions | null, connection: AbstractConnectionAgent, appData: string]\n
Run Code Online (Sandbox Code Playgroud)\n我想将其转换为这样的对象类型:
\ntype StreamAgentParameters = {\n session: SessionAgent\n streamID: string\n isScreenShare: boolean\n connectionID: string\n videoProducerOptions: ProducerOptions | null\n connection: AbstractConnectionAgent\n appData: string\n}\n
Run Code Online (Sandbox Code Playgroud)\n有没有办法做到这一点?
\n我想创建一个工厂函数来测试类以简化设置。
\nexport type Factory<Shape> = (state?: Partial<Shape>) => Shape\n
Run Code Online (Sandbox Code Playgroud)\n我想避免手动输入类的参数,因此我寻找获取构造函数参数的可能性。还有你知道什么吗,还有ConstructorParameters
助手类型。不幸的是,它返回一个元组而不是一个对象。
因此,以下内容不起作用,因为元组不是对象。
\ntype MyClassParameters = ConstructorParameters<typeof MyClass>\n// \xe2\x86\xb5 [session: SessionAgent, streamID: string, isScreenShare: boolean, connectionID: string, videoProducerOptions: ProducerOptions | null, connection: AbstractConnectionAgent, appData: string]\n\nconst createMyClassParameters: Factory<MyClassParameters> = ({\n session = new SessionAgent(randomRealisticSessionID()),\n streamID = randomRealisticStreamID(),\n isScreenShare = false,\n connectionID = randomRealisticConnectionID(),\n videoProducerOptions = createPopulatedProducerOptions(),\n connection = new ConnectionAgent(\n new MockWebSocketConnection(),\n \'IP\',\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ),\n appData = \'test\',\n} = {}) => ({\n session,\n streamID,\n isScreenShare,\n connectionID,\n videoProducerOptions,\n connection,\n appData,\n})\n
Run Code Online (Sandbox Code Playgroud)\n我尝试创建一个将元组转换为对象的辅助类型,但我最好的尝试是这样(但它不起作用)。
\ntype TupleToObject<T extends any[]> = {\n [key in T[0]]: Extract<T, [key, any]>[1]\n}\n
Run Code Online (Sandbox Code Playgroud)\n我怎么解决这个问题?
\n为了将任何元组转换为对象,您可以使用此实用程序类型:
\ntype Reducer<\n Arr extends Array<unknown>,\n Result extends Record<number, unknown> = {},\n Index extends number[] = []\n > =\n Arr extends []\n ? Result\n : Arr extends [infer Head, ...infer Tail]\n ? Reducer<[...Tail], Result & Record<Index[\'length\'], Head>, [...Index, 1]>\n : Readonly<Result>;\n\n// Record<0, "hi"> & Record<1, "hello"> & Record<2, "\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd1\x96\xd1\x82">\ntype Result = Reducer<[\'hi\', \'hello\', \'\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd1\x96\xd1\x82\']>;\n
Run Code Online (Sandbox Code Playgroud)\n由于我们是从元组进行转换,因此您只能使用元素索引作为键。
\n为了保留有关键/索引的信息,我Index
在类型实用程序中添加了额外的泛型类型。每次迭代我都会添加并计算I1
的新长度index
不允许您使用元组标签作为键,因为:
\n\n\n它们\xe2\x80\x99 纯粹用于文档和工具。
\n
正如其他答案中提到的,无法将元组标签转换为字符串文字类型;这些标签仅用于文档,不会影响类型系统:类型[foo: string]
和[bar: string]
以及[string]
都彼此等效。[foo: string]
所以任何要变成的方法{foo: string}
也应该[bar: string]
变成{foo: string}
。所以我们需要放弃捕获元组标签。
元组的真正键是数字字符串,例如"0"
和1"
。如果您只想将元组转换为类似的类型,仅使用那些类似数字的键,而不是所有数组属性和方法,您可以这样做:
type TupleToObject<T extends any[]> = Omit<T, keyof any[]>
Run Code Online (Sandbox Code Playgroud)
这只是使用实用程序Omit<T, K>
类型来忽略所有数组中存在的任何元组属性(例如length
、push
等)。这也或多或少相当于
type TupleToObject<T extends any[]> =
{ [K in keyof T as Exclude<K, keyof any[]>]: T[K] }
Run Code Online (Sandbox Code Playgroud)
它使用显式过滤掉键的映射类型。
这是它在元组类型上的行为方式:
type StreamAgentObjectWithNumericlikeKeys = TupleToObject<StreamAgentParameters>
/* type StreamAgentObjectWithNumericlikeKeys = {
0: SessionAgent;
1: string;
2: boolean;
3: string;
4: ProducerOptions | null;
5: AbstractConnectionAgent;
6: string;
} */
Run Code Online (Sandbox Code Playgroud)
您还可以创建一个函数对实际值执行相同的操作:
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};
Run Code Online (Sandbox Code Playgroud)
如果除了类型元组之外,您还愿意保留属性名称元组,则可以编写一个将数字元组键映射到相应名称的函数:
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
}
*/
Run Code Online (Sandbox Code Playgroud)
您可以创建一个函数对实际值执行相同的操作:
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}
Run Code Online (Sandbox Code Playgroud)