Давайте создадим псевдоним типа AddProp<T, K, V>
, который принимает тип объекта T
, тип ключа K
и тип значения V
и создает новый тип объекта со всеми свойствами из * 1005. * а также новое свойство с ключом K
и значением V
. Вот один из способов его определения:
type AddProp<T, K extends PropertyKey, V> =
(T & { [P in K]: V }) extends infer O ? { [P in keyof O]: O[P] } : never;
Тип (T & {[P in K]: V})
сам по себе сделает эту работу, но в конце вашего цепного вызова вы получите уродливый тип, такой как AddProp<AddProp<{myChar: number;}, "otherChar", number>, "myShort", number>
, поэтому я Я использую трюк с условным типом , который расширяет свойства в один тип объекта, такой как {myChar: number; otherChar: number; myShort: number}
.
Вооружившись этим, давайте начнем переопределять типы Parser
:
class Parser<T = {}> {
private buffer: Buffer;
private offset: number;
private varsInternal: T;
constructor(buffer: Buffer) {
this.offset = 0;
this.buffer = buffer;
this.varsInternal = {} as T;
}
Здесь я установил значение по умолчанию , значение T
быть пустым типом объекта {}
. И мы хотим, чтобы varsInternal
имел тип T
, и мы должны утверждать , что начальное значение {}
для varsInternal
соответствует T
(что не произойдет, если вы вручную укажите T
как в new Parser<{a: number}>(buffer)
... так что не делайте этого).
Теперь давайте добавим закрытый метод для повторного использования, который добавит свойство с ключом типа K
и значением типа V
до this.varsInternal
и возвращаем this
, где мы утверждаем, что возвращенный this
будет иметь тип Parser<AddProp<T, K, V>>
. Идея состоит в том, что это позаботится о мутации типов в других ваших методах:
private addProp<K extends PropertyKey, V>(prop: K, val: V): Parser<AddProp<T, K, V>> {
(this.varsInternal as any)[prop] = val;
return this as any;
}
А вот ваши char()
и short()
методы. Обратите внимание, что они должны быть generi c в свойстве name
, поскольку string
слишком широкий, чтобы отслеживать конкретные ключи, добавленные к T
.
char<K extends PropertyKey>(name: K) {
const val = this.buffer.readUInt8(this.offset);
const ret = this.addProp(name, val);
this.offset += 1;
return ret;
}
short<K extends PropertyKey>(name: K) {
const val = this.buffer.readUInt16BE(this.offset);
const ret = this.addProp(name, val);
this.offset += 2;
return ret;
}
И ваш vars()
такой же:
vars(): T {
return this.varsInternal;
}
}
Давайте проверим это:
declare const myBuffer: Buffer;
const data = new Parser(myBuffer)
.char("myChar")
.char("otherChar")
.short("myShort")
.vars();
/* const data: {
myChar: number;
otherChar: number;
myShort: number;
}*/
console.log(data.myChar); // number
Выглядит хорошо. Восстановленный тип data
равен {myChar: number, otherChar: number, myShort: number}
. Если эти значения должны быть другого типа, вы должны обработать val
внутри char()
и / или short()
перед вызовом this.addProp()
.
В любом случае, я остановлюсь на этом; надеюсь это поможет. Удачи!
Детская площадка ссылка на код