binary-parser
binary-parser copied to clipboard
TypeScript interface is incomplete
In particular, ParserOptions does implement all available options (e.g., length can be a string or function too).
Any update on this? Having the types in the npm package would be helpful so they're always updated.
I haven't had the bandwidth to work on this yet. @types/binary-parser is still more complete than the typings in this repo.
Would you be open to a PR to improve the built-in types based on @types/binary-parser?
@rshea0 Yes it would be very much appreciated! We have improved the typings since I opened this issue so I think the typings are mostly accurate now.
is it expected that e.g. new Parser().uint32('field').parse(buf) returns any? it would be very cool if the returned types could be given actual typescript typings
@cmdcolin Yes, it is expected but a type-safe interface would be very cool. Do you have any good ideas on how to implement it?
This is a very nice feature to have. It is not impossible to implement -- data validation libraries have been doing this for quite some time. However, I have been reading Zod's source code (looks like very advanced stuff), but I haven't yet figured out a way to recreate the same effect. Also, there are other validation libraries, it's just that Zod is the one I'm most familiar with.
See below for an example using Zod, using the IPv4 header parsing example.
Notes:
parsedIpHeaderis theanyobject parsed bybinary-parser.validatedIpHeader,validatedIpHeaderV2, andvalidatedIpHeaderV3are the type-inferred objects validated byzod.- The method-chaining approach takes up a lot of cpu resources to process. I think it's not impossible to implement in
binary-parser, and maybe we can have something like
WhereParser.start() .uint32be("id") ... .uint8("checksum") .end()end()marks the end of the call chain, and it's the only place where the parser types are inferred. - In VS Code, I get this type hint for
validatedIpHeader:const validatedIpHeader: { version: number; headerLength: number; tos: number; packetLength: number; id: number; offset: number; fragOffset: number; ttl: number; protocol: number; checksum: number; src: number[]; dst: number[]; }
Example (long block of code)
// Module import
const Parser = require('binary-parser').Parser;
// Alternative way to import the module
// import { Parser } from 'binary-parser';
// Build an IP packet header Parser
const ipHeader = new Parser()
.endianness('big')
.bit4('version')
.bit4('headerLength')
.uint8('tos')
.uint16('packetLength')
.uint16('id')
.bit3('offset')
.bit13('fragOffset')
.uint8('ttl')
.uint8('protocol')
.uint16('checksum')
.array('src', {
type: 'uint8',
length: 4,
})
.array('dst', {
type: 'uint8',
length: 4,
});
// Prepare buffer to parse.
const buf = Buffer.from('450002c5939900002c06ef98adc24f6c850186d1', 'hex');
// Parse buffer and show result
console.log(ipHeader.parse(buf));
// preparation
import { z } from 'zod';
const parsedIpHeader = ipHeader.parse(buf);
// typical Zod usage
const ipHeaderValidator = z.object({
version: z.number(),
headerLength: z.number(),
tos: z.number(),
packetLength: z.number(),
id: z.number(),
offset: z.number(),
fragOffset: z.number(),
ttl: z.number(),
protocol: z.number(),
checksum: z.number(),
src: z.array(z.number()).length(4),
dst: z.array(z.number()).length(4),
});
const validatedIpHeader = ipHeaderValidator.parse(parsedIpHeader);
console.log(validatedIpHeader);
// chained -- non-typical, vscode takes a long time processing this
const ipHeaderValidatorV2 = z
.object({})
.extend({ version: z.number() })
.extend({ headerLength: z.number() })
.extend({ tos: z.number() })
.extend({ packetLength: z.number() })
.extend({ id: z.number() })
.extend({ offset: z.number() })
.extend({ fragOffset: z.number() })
.extend({ ttl: z.number() })
.extend({ protocol: z.number() })
// .extend({ checksum: z.number() })
// .extend({ src: z.array(z.number()).length(4) })
// .extend({ dst: z.array(z.number()).length(4) });
// if the last 3 properties are uncommented, tsc will emit an error:
// error TS2589: Type instantiation is excessively deep and possibly infinite.
const validatedIpHeaderV2 = ipHeaderValidatorV2.parse(parsedIpHeader);
console.log(validatedIpHeaderV2);
// this works, but type hint is delayed, and vscode hogs cpu processing this
const ipHeaderValidatorV3 = z
.object({})
.merge(z.object({ version: z.number() }))
.merge(z.object({ headerLength: z.number() }))
.merge(z.object({ tos: z.number() }))
.merge(z.object({ packetLength: z.number() }))
.merge(z.object({ id: z.number() }))
.merge(z.object({ offset: z.number() }))
.merge(z.object({ fragOffset: z.number() }))
.merge(z.object({ ttl: z.number() }))
.merge(z.object({ protocol: z.number() }))
.merge(z.object({ checksum: z.number() }))
.merge(z.object({ src: z.array(z.number()).length(4) }))
.merge(z.object({ dst: z.array(z.number()).length(4) }));
const validatedIpHeaderV3 = ipHeaderValidatorV3.parse(parsedIpHeader);
console.log(validatedIpHeaderV3);