```typescript
/**
 * Parse a semantic version string.
 *
 * @param {string} version - A semantic version string of the form MAJOR.MINOR.PATCH with an optional -prerelease and an optional +build.
 */
export function parseSemver(version: string): {
  major: number;
  minor: number;
  patch: number;
  prerelease: string | null;
  build: string | null;
} {
  const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(\w+))?(?:+\S*)?$/);
  if (!match) {
    throw new Error(`Invalid semver format: ${version}`);
  }

  const [major, minor, patch] = match.slice(1).map(Number);
  if (isNaN(major) || isNaN(minor) || isNaN(patch)) {
    throw new Error('Major, minor and patch versions must be numbers');
  }
  if (major < 0 || major > 999 || minor < 0 || minor > 999 || patch < 0 || patch > 999) {
    throw new Error('Major, minor and patch versions must be between 0 and 999');
  }

  const prerelease = match[5];
  const build = match.slice(6).join('');

  return { major, minor, patch, prerelease, build };
}
```