基于Typescript中的对象数组创建对象类型

基于Typescript中的对象数组创建对象类型,typescript,Typescript,我有一个用js编写的库,可以帮助打包和解包Buffers。它使用生成器模式: const bufferFormat=buffer() .int8('id')) .int16(“子通道”) .int32(“通道”) .int32('connectionId')) .varString(“消息”); 常量buff=缓冲区。从(…); const obj=bufferFormat.unpack(buff); /* obj的类型==={ id:编号; 子通道:编号; 频道:号码; connectionI

我有一个用js编写的库,可以帮助打包和解包
Buffer
s。它使用生成器模式:

const bufferFormat=buffer()
.int8('id'))
.int16(“子通道”)
.int32(“通道”)
.int32('connectionId'))
.varString(“消息”);
常量buff=缓冲区。从(…);
const obj=bufferFormat.unpack(buff);
/*
obj的类型==={
id:编号;
子通道:编号;
频道:号码;
connectionId:数字;
消息:字符串;
}
*/
const buffOut=bufferFormat.pack({
id:1,
子频道:2,
连接ID:3,
消息:“消息”,
});
我想给它添加类型,理想情况下让它隐式地计算出对象类型。我考虑这样做的方式是将格式化程序从生成器样式更改为在构造函数中采用一系列格式:

type FormatArrayElement<K extends keyof any> = { name: K, type: keyof TypeMapping };
const bufferFormat=新的bufferFormat([
{
键入:“int8”,
名称:“id”,
},
{
类型:“int16”,
名称:'子频道',
},
{
键入:“字符串”,
名称:'消息',
},
]);
然后隐式地确定要保存的包/解包对象类型

接口{
id:编号;
子频道:号码,
消息:string,
}

这需要我能够从对象数组中推断对象类型。这可以用typescript实现吗?或者我必须想出另一种方法来将类型添加到此缓冲区格式化程序吗?

您不必更改实现,可以将类型赋予原始生成器对象,以便编译器能够推断
解包的结果。我们在
BufferBuilder
的通用参数中收集名称和二进制类型,并且
unpack
使用该参数使用
PropTypeMap
将其转换为对象类型:

interface PropTypeMap {
    int8: number;
    int16: number;
    int32: number;
    varString: string;
}

type PropType = keyof PropTypeMap;

interface BufferBuilder<PropTypes extends { [p in string]: PropType }> {
    int8<N extends string>(n: N): BufferBuilder<PropTypes & { [n in N]: 'int8' }>;
    int16<N extends string>(n: N): BufferBuilder<PropTypes & { [n in N]: 'int16' }>;
    int32<N extends string>(n: N): BufferBuilder<PropTypes & { [n in N]: 'int32' }>;
    varString<N extends string>(n: N): BufferBuilder<PropTypes & { [n in N]: 'varString' }>;

    unpack(buffer: Buffer): { [p in keyof PropTypes]: PropTypeMap[PropTypes[p]] };
    pack(obj: { [p in keyof PropTypes]: PropTypeMap[PropTypes[p]] }): Buffer;
} 

declare function buffer(): BufferBuilder<{}>;
// typed implementation left as exercise


const bufferFormat = buffer()
    .int8('id')
    .int16('subchannel')
    .int32('channel')
    .int32('connectionId')
    .varString('message');


const obj = bufferFormat.unpack({} as Buffer);     // inferred as const obj: { id: number; subchannel: number; channel: number; connectionId: number; message: string; }
接口PropTypeMap{
int8:数字;
int16:数字;
int32:数字;
varString:string;
}
类型PropType=PropTypeMap的键;
接口缓冲生成器{
int8(n:n):缓冲区生成器;
int16(n:n):缓冲区生成器;
int32(n:n):缓冲区生成器;
varString(n:n):缓冲区生成器;
解包(buffer:buffer):{[p in-keyof-PropTypes]:PropTypeMap[PropTypes[p]};
pack(obj:{[p in-keyof-PropTypes]:PropTypeMap[PropTypes[p]}):缓冲区;
} 
声明函数buffer():BufferBuilder;
//类型化实现留作练习
const bufferFormat=buffer()
.int8('id'))
.int16(“子通道”)
.int32(“通道”)
.int32('connectionId'))
.varString(“消息”);
const obj=bufferFormat.unpack({}作为缓冲区);//推断为常量对象:{id:number;子通道:number;通道:number;connectionId:number;消息:string;}

您不必更改实现,可以为原始生成器对象指定类型,以便编译器能够推断
解包的结果。我们在
BufferBuilder
的通用参数中收集名称和二进制类型,并且
unpack
使用该参数使用
PropTypeMap
将其转换为对象类型:

interface PropTypeMap {
    int8: number;
    int16: number;
    int32: number;
    varString: string;
}

type PropType = keyof PropTypeMap;

interface BufferBuilder<PropTypes extends { [p in string]: PropType }> {
    int8<N extends string>(n: N): BufferBuilder<PropTypes & { [n in N]: 'int8' }>;
    int16<N extends string>(n: N): BufferBuilder<PropTypes & { [n in N]: 'int16' }>;
    int32<N extends string>(n: N): BufferBuilder<PropTypes & { [n in N]: 'int32' }>;
    varString<N extends string>(n: N): BufferBuilder<PropTypes & { [n in N]: 'varString' }>;

    unpack(buffer: Buffer): { [p in keyof PropTypes]: PropTypeMap[PropTypes[p]] };
    pack(obj: { [p in keyof PropTypes]: PropTypeMap[PropTypes[p]] }): Buffer;
} 

declare function buffer(): BufferBuilder<{}>;
// typed implementation left as exercise


const bufferFormat = buffer()
    .int8('id')
    .int16('subchannel')
    .int32('channel')
    .int32('connectionId')
    .varString('message');


const obj = bufferFormat.unpack({} as Buffer);     // inferred as const obj: { id: number; subchannel: number; channel: number; connectionId: number; message: string; }
接口PropTypeMap{
int8:数字;
int16:数字;
int32:数字;
varString:string;
}
类型PropType=PropTypeMap的键;
接口缓冲生成器{
int8(n:n):缓冲区生成器;
int16(n:n):缓冲区生成器;
int32(n:n):缓冲区生成器;
varString(n:n):缓冲区生成器;
解包(buffer:buffer):{[p in-keyof-PropTypes]:PropTypeMap[PropTypes[p]};
pack(obj:{[p in-keyof-PropTypes]:PropTypeMap[PropTypes[p]}):缓冲区;
} 
声明函数buffer():BufferBuilder;
//类型化实现留作练习
const bufferFormat=buffer()
.int8('id'))
.int16(“子通道”)
.int32(“通道”)
.int32('connectionId'))
.varString(“消息”);
const obj=bufferFormat.unpack({}作为缓冲区);//推断为常量对象:{id:number;子通道:number;通道:number;connectionId:number;消息:string;}

我假设您在实现方面不需要帮助;你只是想把这些类型弄清楚。像这样的怎么样

首先定义从名称(如
“int16”
)到TypeScript对应类型的映射:

type TypeMapping = {
  int8: number,
  int16: number,
  string: string,
  // ... add everything you need here
}
然后描述将传递到
BufferFormat
构造函数的数组元素:

type FormatArrayElement<K extends keyof any> = { name: K, type: keyof TypeMapping };
这可能有点难以消化。基本上,您使用遍历每个
名称
属性,并生成由
类型映射
指定的类型的值

现在,最后,我们可以声明
BufferFormat
类的类型:

declare class BufferFormat<K extends keyof any, F extends FormatArrayElement<K>> {
  constructor(map: F[]);
  unpack(buffer: Buffer): TypeFromFormatArrayElement<F>;
  pack(obj: TypeFromFormatArrayElement<F>): Buffer
}
真管用!如果您试图直接检查
b
的类型,它将显示为无效类型别名:

const b: TypeFromFormatArrayElement<{
    type: "int8";
    name: "id";
} | {
    type: "int16";
    name: "subchannel";
} | {
    type: "string";
    name: "message";
}>
const b:TypeFromFormatArrayElement
但是如果你检查实际的属性,一切都会如你所期望的那样


希望有帮助;祝你好运

我假设您在实施方面不需要帮助;你只是想把这些类型弄清楚。像这样的怎么样

首先定义从名称(如
“int16”
)到TypeScript对应类型的映射:

type TypeMapping = {
  int8: number,
  int16: number,
  string: string,
  // ... add everything you need here
}
然后描述将传递到
BufferFormat
构造函数的数组元素:

type FormatArrayElement<K extends keyof any> = { name: K, type: keyof TypeMapping };
这可能有点难以消化。基本上,您使用遍历每个
名称
属性,并生成由
类型映射
指定的类型的值

现在,最后,我们可以声明
BufferFormat
类的类型:

declare class BufferFormat<K extends keyof any, F extends FormatArrayElement<K>> {
  constructor(map: F[]);
  unpack(buffer: Buffer): TypeFromFormatArrayElement<F>;
  pack(obj: TypeFromFormatArrayElement<F>): Buffer
}
真管用!如果您试图直接检查
b
的类型,它将显示为无效类型别名:

const b: TypeFromFormatArrayElement<{
    type: "int8";
    name: "id";
} | {
    type: "int16";
    name: "subchannel";
} | {
    type: "string";
    name: "message";
}>
const b:TypeFromFormatArrayElement
但是如果你检查实际的属性,一切都会如你所期望的那样


希望有帮助;祝你好运

这真的很酷,我没有意识到typescript支持这种“递归”类型