• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

I Like Kill Nerds

The blog of Australian Front End / Aurelia Javascript Developer & brewing aficionado Dwayne Charrington // Aurelia.io Core Team member.

  • Home
  • Aurelia 2
  • Aurelia 1
  • About
  • Aurelia 2 Consulting/Freelance Work

How to parse VIN numbers using TypeScript

TypeScript · May 19, 2023

In this blog post, we’ll create a simple TypeScript module for parsing Vehicle Identification Numbers (VINs). VIN is a unique 17-character code the automotive industry uses to identify individual vehicles.

To get started, we’ll need to install TypeScript and Jest:

npm install typescript jest @types/jest ts-jest

Next, configure Jest to work with TypeScript by creating a jest.config.js file:

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
};

Now, let’s write the TypeScript code for parsing VINs. Create a file named vinParser.ts:

export interface VINInfo {
  wmi: string;
  vds: string;
  vis: string;
  manufacturer: string;
  modelYear: number;
  plantCode: string;
}

export class VINParser {
  private static readonly VIN_REGEX = /^(?:(?![IiOoQq])[A-HJ-NPR-Za-hj-npr-z0-9]){17}$/;
  private static readonly WMI_MAP = {
    "1HG": "Honda",
    "1N4": "Nissan",
    "1FA": "Ford",
    "1FT": "Ford",
    "1G1": "Chevrolet",
    "1G6": "Cadillac",
    "1GC": "Chevrolet",
    "2HG": "Honda",
    "2T1": "Toyota",
    "3N1": "Nissan",
    "4T1": "Toyota",
    "5J6": "Honda",
    "5N1": "Nissan",
    "5TDB": "Toyota",
    "5TF": "Toyota",
    "JHM": "Honda",
    "JN1": "Nissan",
    "JT3": "Toyota",
    "JTD": "Toyota",
    "WBA": "BMW",
    "WBS": "BMW M",
    "WDB": "Mercedes-Benz",
    "WP0": "Porsche",
    "ZAM": "Maserati",
    "KM8": "Hyundai",
    "KMH": "Hyundai",
    "5NP": "Hyundai",
    // Add more World Manufacturer Identifiers and their corresponding manufacturers here
  };

  private static readonly YEAR_MAP = {
    "1": 2001,
    "2": 2002,
    "3": 2003,
    "4": 2004,
    "5": 2005,
    "6": 2006,
    "7": 2007,
    "8": 2008,
    "9": 2009,
    "A": 2010,
    "B": 2011,
    "C": 2012,
    "D": 2013,
    "E": 2014,
    "F": 2015,
    "G": 2016,
    "H": 2017,
    "J": 2018,
    "K": 2019,
    "L": 2020,
    "M": 2021,
    "N": 2022,
    "P": 2023,
    "R": 2024,
    "S": 2025,
    "T": 2026,
    "V": 2027,
    "W": 2028,
    "X": 2029,
    "Y": 2030,
    "1": 2031,
    "2": 2032,
    "3": 2033,
    "4": 2034,
    "5": 2035,
    "6": 2036,
    "7": 2037,
    "8": 2038,
    "9": 2039,
    // This map can be extended for years beyond 2039, following the pattern established by the ISO standards
  };

  public static parse(vin: string): VINInfo | null {
    if (!this.validate(vin)) {
      return null;
    }

    const wmi = vin.slice(0, 3);
    const vds = vin.slice(3, 9);
    const vis = vin.slice(9, 17);
    const manufacturer = this.WMI_MAP[wmi] || "Unknown";
    const modelYear = this.YEAR_MAP[vin[9].toUpperCase() as keyof typeof VINParser.YEAR_MAP] || 0;
    const plantCode = vin[10];

    return {
      wmi,
      vds,
      vis,
      manufacturer,
      modelYear,
      plantCode,
    };
  }

  public static validate(vin: string): boolean {
    return this.VIN_REGEX.test(vin);
  }
}

In this code, we define a class VINParser with a single method validate. It takes a VIN string as input and returns a boolean indicating whether the VIN is valid. We use a regular expression to ensure the VIN is 17 characters long and contains no illegal characters (I, O, and Q).

Remember that this map will need to be updated in 2040 and beyond, but the current ISO standard doesn’t provide a direct mapping for those years. The best approach for handling the model year would be to follow the ISO standards and update the map when new rules are defined.

The code above includes more World Manufacturer Identifiers (WMIs) for common manufacturers. You can further extend the WMI_MAP as needed to include additional manufacturers or more specific plant codes.

Now, let’s write Jest unit tests for the VINParser class. Create a file named vinParser.test.ts:

import { VINParser } from './vinParser';

describe('VINParser', () => {
  test('should return true for valid VINs', () => {
    const validVins = [
      '1FTEW1EP9GKF12229',
      '1FTFX1EF2GKD50050',
      '5J6RM3H32EL012345',
    ];

    for (const vin of validVins) {
      expect(VINParser.validate(vin)).toBe(true);
    }
  });

  test('should return false for invalid VINs', () => {
    const invalidVins = [
      '1FTEW1EP9GKF1222', // Too short
      '1FTFX1EF2GKD5005Q', // Contains an illegal character
      '5J6RM3H32EL01234X', // Too long
    ];

    for (const vin of invalidVins) {
      expect(VINParser.validate(vin)).toBe(false);
    }
  });
  
  test('should parse valid VINs', () => {
    const validVins = [
      {
        vin: '1FTEW1EP9GKF12229',
        manufacturer: 'Ford',
        modelYear: 2016,
        plantCode: 'K',
      },
      {
        vin: '5J6RM3H32EL012345',
        manufacturer: 'Honda',
        modelYear: 2014,
        plantCode: 'L',
      },
      {
        vin: 'KMHGC4DD8CU012345',
        manufacturer: 'Hyundai',
        modelYear: 2012,
        plantCode: 'U',
      },
    ];

    for (const { vin, manufacturer, modelYear, plantCode } of validVins) {
      const parsedVin = VINParser.parse(vin);
      expect(parsedVin).not.toBeNull();
      expect(parsedVin!.manufacturer).toBe(manufacturer);
      expect(parsedVin!.modelYear).toBe(modelYear);
      expect(parsedVin!.plantCode).toBe(plantCode);
    }
  });
  
  test('should return null for invalid VINs', () => {
    const invalidVins = [
      '1FTEW1EP9GKF1222', // Too short
      '1FTFX1EF2GKD5005Q', // Contains an illegal character
      '5J6RM3H32EL01234X', // Too long
    ];

    for (const vin of invalidVins) {
      expect(VINParser.parse(vin)).toBeNull();
    }
  });
});

To run the tests, add a script to your package.json file:

{
  "scripts": {
    "test": "jest"
  }
}

Now you can run the tests with npm test.

You can further extend this code to parse additional information from the VIN, such as the manufacturer, model year, and production plant. You can find more information about the structure of VINs here.

Dwayne

Leave a Reply Cancel reply

0 Comments
Inline Feedbacks
View all comments

Primary Sidebar

Popular

  • Thoughts on the Flipper Zero
  • The Quad Cortex Desktop Editor is Finally Announced
  • Handling Errors with the Fetch API
  • How To Get The Hash of A File In Node.js
  • How To Install Eufy Security Cameras Without Drilling or Using Screws

Copyright © 2023 · Dwayne Charrington · Log in

wpDiscuz