quadscan

TypeScript patterns

All public types are re-exported from quadscan. No dist/types/... hopping required.

Narrowing ScanResult.success

ScanResult.success is a discriminant. TypeScript narrows the rest of the result when you check it:

import { Quadscan } from 'quadscan';

const r = await Quadscan.scan(file, { mode: 'extract' });

if (r.success) {
  // r.corners is `CornerPoints` (non-null)
  // r.output is the warped image, never null
  r.corners.topLeft.x;     // ok
  r.output;                // HTMLCanvasElement | ImageData | string
} else {
  // r.message is the failure reason
  console.warn(r.message);
}

If you want a stricter signal, the canonical pattern is to write a guard:

import type { ScanResult } from 'quadscan';

interface SuccessResult extends ScanResult {
  success: true;
  corners: NonNullable<ScanResult['corners']>;
  confidence: number;
}

function isSuccess(r: ScanResult): r is SuccessResult {
  return r.success && r.corners !== null;
}

Re-exporting library types

If your app passes corners across module boundaries, alias the types in a shared module:

// types/scan.ts
export type {
  CornerPoint,
  CornerPoints,
  CornerPointsInput,
  ScanResult,
  ScanOptions,
} from 'quadscan';

Then import type { CornerPoints } from '@/types/scan' everywhere. Easier to refactor later (e.g. wrap with your own shape).

using declarations

Quadscan implements Symbol.dispose. With TypeScript 5.2+ and a target of ES2022 or later you can scope an instance:

{
  using q = new Quadscan({ model: 'fastvit' });
  await q.initialize();
  for (const img of images) {
    const r = await q.scan(img, { mode: 'extract' });
    // ...
  }
} // q.dispose() called automatically here

Particularly handy in routes / page-level handlers where you want a fresh session per page view.

Strict input types

ScanInput is the union of accepted shapes:

import type { ScanInput } from 'quadscan';

function preprocess(input: ScanInput) {
  // ...
}

If you receive untyped data (e.g. from an event), narrow before calling scan:

const data: unknown = getFromSomewhere();
if (
  data instanceof Blob ||
  data instanceof File ||
  data instanceof HTMLImageElement ||
  data instanceof HTMLCanvasElement
) {
  await Quadscan.scan(data);
}

Quadscan.scan throws InvalidInputError on a bad shape. Narrowing upstream is just nicer DX.