logistics.hs-code-validate@1.0.0
impl/typescript.ts
1,575 bytes · the TypeScript implementation · view raw
import { type HsCodeCheck } from "./logistics_hs_code_validate_types.ts";
const KINDS: { readonly [length: number]: string } = { 6: "hs", 8: "cn", 10: "taric" };
function invalid(reason: string): HsCodeCheck {
return { valid: false, normalised: null, kind: null, chapter: null, heading: null, subheading: null, reason };
}
/**
* Check the format of a 6-digit HS, 8-digit CN or 10-digit TARIC/UK commodity
* code. Validators answer rather than throw: a malformed code is the answer "no".
*/
export function validateHsCode(value: string): HsCodeCheck {
if (typeof value !== "string") return invalid("empty");
let digits = "";
for (const ch of value) {
if (ch === " " || ch === ".") continue;
if (ch < "0" || ch > "9") return invalid("bad-character");
digits += ch;
}
if (digits.length === 0) return invalid("empty");
const kind = KINDS[digits.length];
if (kind === undefined) return invalid("bad-length");
// Text, not a number: chapter "01" must keep its zero.
const chapter = digits.slice(0, 2);
if (chapter === "00") return invalid("bad-chapter");
if (chapter === "77") return invalid("reserved-chapter");
// 98 and 99 are left to national tariffs, so only a national-length code may use them.
if ((chapter === "98" || chapter === "99") && kind === "hs") return invalid("national-chapter");
if (digits.slice(2, 4) === "00") return invalid("bad-heading");
return {
valid: true,
normalised: digits,
kind,
chapter,
heading: digits.slice(0, 4),
subheading: digits.slice(0, 6),
reason: null,
};
}