```ts
export function cappedCompositions(total: number, parts: number, lo: number, hi: number): number {
  if (!Number.isInteger(parts) || parts <= 0) throw new Error('Parts must be a positive integer');
  if (!Number.isInteger(lo) || !Number.isInteger(hi) || lo < 0 || hi < 0) throw new Error('Bounds must be non-negative integers');
  if (lo > hi) throw new Error('Lower bound cannot be greater than upper bound');
  if (!Number.isInteger(total)) throw new Error('Total must be an integer');

  const dp: number[][] = Array.from({ length: parts + 1 }, () => Array(total + 1).fill(0));
  dp[0][0] = 1;

  for (let i = 1; i <= parts; i++) {
    for (let j = 0; j <= total; j++) {
      for (let k = lo; k <= hi && k <= j; k++) {
        dp[i][j] += dp[i - 1][j - k];
      }
    }
  }

  return dp[parts][total];
}
```