5

Se me permite algumas sugestões:

Não precisa usar split, dá para percorrer a string em um loop simples e usar charCodeAt(indice) para obter o caractere da posição desejada. E não precisa usar uma expressão regular para ver se cada caractere é um dígito, pois charCodeAt vai funcionar tanto para caracteres que são dígitos quanto para as letras.

Também não vejo necessidade de reduce quando um loop simples resolve. Claro que para poucas validações tanto faz, mas reduce tem um overhead que pode fazer diferença caso precise rodar milhões de casos. Sem contar que - pelo menos na minha opinião - um loop simples deixa o código mais claro (mas aí admito que já é questão de gosto).

Outro ponto é que no cálculo do segundo dígito verificador vc converte dv1 para string, somente para obter o seu valor numérico de novo. É uma volta desnecessária (e entendo que só foi preciso fazer isso para poder usar dentro do reduce, pois o array deve ser de caracteres).

Por fim, podemos manter apenas um array de pesos, já que somente a primeira posição é diferente (o cálculo do primeiro dígito não usa a primeira posição, e o do segundo dígito usa todas). Ficaria assim:

function dv(sum) {
    const resto = sum % 11;
    return resto < 2 ? 0 : 11 - resto;
}

function calcDv(cnpjBase: string): [number, number] {
    const weights = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
    var sum1 = 0, sum2 = 0;
    // calcula as duas somas simultaneamente, no mesmo loop
    // o que muda é o peso usado por cada uma
    for (let i = 0; i < cnpjBase.length; i++) {
        const val = cnpjBase.charCodeAt(i) - 48;
        sum1 += val * weights[i + 1];
        sum2 += val * weights[i];
    }

    const dv1 = dv(sum1);
    // completa sum2 com o peso que faltava, e já retorna os dígitos
    return [dv1, dv(sum2 + dv1 * 2)];
}

console.log(calcDv('12ABC34501DE')); // [3, 5]

E também coloquei ponto-e-vírgula no final das linhas. Pode parecer "frescura", e sei que o TypeScript (e também o JavaScript) "aceita" o código sem ponto e vírgula e "funciona", mas isso evita algumas situações bizarras que podem ocorrer se você não usá-los, como essa e essa (veja mais sobre isso aqui).


Só por curiosidade, fiz um teste usando o Benchmark.js para comparar as duas soluções. O resultado foi:

sem reduce x 36,216,689 ops/sec ±0.92% (92 runs sampled)
com reduce x 1,554,090 ops/sec ±0.65% (96 runs sampled)
Fastest is sem reduce

Os números acima são de operações por segundo, ou seja, quanto maior, melhor. Repare que se não usar reduce (como eu fiz) ele executou 36 milhões de operações por segundo, enquanto com reduce (mais o split e transformações de/para string, regex para ver se é dígito, etc) faz apenas 1 milhão e meio. Ou seja, essa simples alteração melhorou o desempenho em 23 vezes.

Também fiz esse mesmo teste no jsbench.me e o resultado foi similar: o código sem reduce fez 18 milhões de operações por segundo, enquanto com reduce fez 1,6 milhões.

Novamente, claro que se for para validar poucos CNPJ's (ou até se for centenas, ou poucos milhares), a diferença será insignificante. Mas se for usar para validar bases maiores (milhares ou milhões de registros, por exemplo), pode fazer diferença.

Carregando publicação patrocinada...