binary-parser icon indicating copy to clipboard operation
binary-parser copied to clipboard

TypeScript interface is incomplete

Open keichi opened this issue 6 years ago • 8 comments

In particular, ParserOptions does implement all available options (e.g., length can be a string or function too).

keichi avatar Jun 06 '19 12:06 keichi

Any update on this? Having the types in the npm package would be helpful so they're always updated.

mohd-akram avatar Apr 20 '20 12:04 mohd-akram

I haven't had the bandwidth to work on this yet. @types/binary-parser is still more complete than the typings in this repo.

keichi avatar Apr 27 '20 03:04 keichi

Would you be open to a PR to improve the built-in types based on @types/binary-parser?

shayded-exe avatar Mar 27 '22 03:03 shayded-exe

@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.

keichi avatar Apr 04 '22 09:04 keichi

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 avatar Jul 01 '22 20:07 cmdcolin

@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?

keichi avatar Jul 03 '22 12:07 keichi

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:

  1. parsedIpHeader is the any object parsed by binary-parser.
  2. validatedIpHeader, validatedIpHeaderV2, and validatedIpHeaderV3 are the type-inferred objects validated by zod.
  3. 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
    Parser.start()
    .uint32be("id")
    ... 
    .uint8("checksum")
    .end()
    
    Where end() marks the end of the call chain, and it's the only place where the parser types are inferred.
  4. 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);

wpyoga avatar Sep 06 '22 16:09 wpyoga