Ferramentas gratuitas para desenvolvedores. Nenhum dado real de pessoas é consultado ou armazenado.Como funciona o algoritmo do CPF →
TypeScript

Validar, gerar e formatar CNPJ em TypeScript

Validação de CNPJ numérico e alfanumérico em TypeScript com tipo de marca e motivo da recusa, geração, formatação, testes no Vitest e API.

Roberto GuerraPublicado em 05 de outubro de 2026Editar no GitHub

A lógica para validar CNPJ em TypeScript é a mesma da versão em JavaScript, mas os tipos ajudam a atravessar a mudança para o CNPJ alfanumérico com menos sustos. Aqui a validação devolve um resultado discriminado, com o formato detectado e o motivo da recusa, e marca o CNPJ aprovado com um tipo próprio. Funções de domínio passam a exigir Cnpj em vez de string, e o compilador aponta cada lugar que ainda recebe texto sem checagem. O código compila com strict e não usa pacotes.

Validar CNPJ em TypeScript (numérico e alfanumérico)

cnpj.ts
export type Cnpj = string & { readonly __marca: 'Cnpj' };
export type FormatoCnpj = 'numerico' | 'alfanumerico';

export type ResultadoCnpj =
| { valido: true; cnpj: Cnpj; formato: FormatoCnpj }
| { valido: false; motivo: 'formato' | 'repetido' | 'digito' };

const PESOS_1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
const PESOS_2 = [6, ...PESOS_1];

function calcularDigito(base: string, pesos: readonly number[]): number {
let soma = 0;
for (let i = 0; i < base.length; i++) {
  soma += (base.charCodeAt(i) - 48) * pesos[i];
}
const resto = soma % 11;
return resto < 2 ? 0 : 11 - resto;
}

export function analisarCnpj(valor: string): ResultadoCnpj {
const cnpj = valor.toUpperCase().replace(/[.\/\-\s]/g, '');
if (!/^[A-Z0-9]{12}[0-9]{2}$/.test(cnpj)) return { valido: false, motivo: 'formato' };
if (/^(.)\1{11}/.test(cnpj)) return { valido: false, motivo: 'repetido' };
const base = cnpj.slice(0, 12);
const dv1 = calcularDigito(base, PESOS_1);
const dv2 = calcularDigito(base + dv1, PESOS_2);
if (cnpj.slice(12) !== String(dv1) + String(dv2)) return { valido: false, motivo: 'digito' };
const formato: FormatoCnpj = /^[0-9]+$/.test(cnpj) ? 'numerico' : 'alfanumerico';
return { valido: true, cnpj: cnpj as Cnpj, formato };
}

export function validarCnpj(valor: string): boolean {
return analisarCnpj(valor).valido;
}

O fluxo, em ordem:

  1. Converte para maiúsculas e remove pontos, barra, hífen e espaços. Qualquer outro caractere cai em formato.
  2. Exige 12 caracteres de A-Z0-9 seguidos de dois dígitos: letras só valem na raiz e na ordem.
  3. Recusa as bases com os 12 primeiros caracteres iguais, como 00.000.000/0000-00.
  4. Converte cada caractere com charCodeAt(i) - 48 (dígitos valem 0 a 9, A vale 17, Z vale 42) e aplica os pesos de 5 a 2 e 9 a 2, depois de 6 a 2 e 9 a 2.

Para 11.222.333/0001-81 as somas são 102 e 120, os restos 3 e 10, e os dígitos 8 e 1. Para 12.ABC.345/01DE-35 as somas são 459 e 424, os restos 8 e 6, e os dígitos 3 e 5. Depois de if (r.valido), o TypeScript sabe que r.cnpj e r.formato existem; no outro ramo, só r.motivo.

Gerar CNPJ válido em TypeScript

cnpj.ts
const ALFABETOS: Record<FormatoCnpj, string> = {
numerico: '0123456789',
alfanumerico: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
};

export function gerarCnpj(opcoes: { formato?: FormatoCnpj; formatado?: boolean } = {}): string {
const alfabeto = ALFABETOS[opcoes.formato ?? 'numerico'];
let base: string;
do {
  const sorteio = crypto.getRandomValues(new Uint32Array(12));
  base = Array.from(sorteio, (x) => alfabeto[x % alfabeto.length]).join('');
} while (/^(.)\1{11}$/.test(base));
const dv1 = calcularDigito(base, PESOS_1);
const dv2 = calcularDigito(base + dv1, PESOS_2);
const cnpj = base + dv1 + dv2;
return opcoes.formatado ? formatarCnpj(cnpj) : cnpj;
}

O laço descarta bases repetidas antes de calcular os verificadores. O número gerado é válido pelo algoritmo, mas pode coincidir com um CNPJ real por acaso; use-o só em testes. Para exemplos prontos com letras em posições variadas, veja os exemplos de CNPJ alfanumérico para teste.

Formatar e limpar CNPJ em TypeScript

cnpj.ts
export function limparCnpj(valor: string): string {
return valor.toUpperCase().replace(/[^A-Z0-9]/g, '');
}

export function formatarCnpj(valor: string): string {
const c = limparCnpj(valor).slice(0, 14);
let saida = c.slice(0, 2);
if (c.length > 2) saida += '.' + c.slice(2, 5);
if (c.length > 5) saida += '.' + c.slice(5, 8);
if (c.length > 8) saida += '/' + c.slice(8, 12);
if (c.length > 12) saida += '-' + c.slice(12, 14);
return saida;
}

formatarCnpj é idempotente e sempre devolve maiúsculas: '12abc34501de35' e '12.ABC.345/01DE-35' resultam em '12.ABC.345/01DE-35'. Para gravar, use o cnpj devolvido por analisarCnpj, que já vem limpo e validado. Se o seu banco guarda CNPJ em coluna numérica, ela precisa virar texto de 14 caracteres; o guia CNPJ alfanumérico: o que muda no seu sistema lista os pontos de atenção.

Testes

cnpj.test.ts
import { describe, expect, it } from 'vitest';
import { analisarCnpj, formatarCnpj, gerarCnpj, validarCnpj } from './cnpj';

describe('validarCnpj', () => {
it.each([
  { cnpj: '11.222.333/0001-81', esperado: true },
  { cnpj: '12.ABC.345/01DE-35', esperado: true },
  { cnpj: '11.222.333/0001-82', esperado: false },
  { cnpj: '00.000.000/0000-00', esperado: false },
])('$cnpj → $esperado', ({ cnpj, esperado }) => {
  expect(validarCnpj(cnpj)).toBe(esperado);
});

it('explica o motivo e o formato', () => {
  expect(analisarCnpj('00.000.000/0000-00')).toEqual({ valido: false, motivo: 'repetido' });
  expect(analisarCnpj('11.222.333/0001-82')).toEqual({ valido: false, motivo: 'digito' });
  expect(analisarCnpj('12.abc.345/01de-35')).toMatchObject({ valido: true, formato: 'alfanumerico' });
});
});

describe('gerarCnpj e formatarCnpj', () => {
it.each(['numerico', 'alfanumerico'] as const)('gera CNPJs válidos (%s)', (formato) => {
  for (let i = 0; i < 1000; i++) expect(validarCnpj(gerarCnpj({ formato }))).toBe(true);
});
it('formata de forma idempotente', () => {
  expect(formatarCnpj(formatarCnpj('12abc34501de35'))).toBe('12.ABC.345/01DE-35');
});
});

Usar a API em vez de reimplementar

A API do cpf.dev.br faz a mesma validação por HTTP, sem chave e com limite de 60 requisições por minuto por IP. Envie o CNPJ sem máscara, ou com a barra codificada como %2F. O export {} transforma o arquivo em módulo e libera o await no nível superior.

api.ts
export {};

interface RespostaValidacaoCnpj {
valido: boolean;
cnpj: string;
formatado: string;
formato: 'numerico' | 'alfanumerico';
raiz: string;
ordem: string;
matriz: boolean;
}

const resposta = await fetch('https://www.cpf.dev.br/api/v1/cnpj/validar/12ABC34501DE35');
if (!resposta.ok) throw new Error('HTTP ' + resposta.status);
const dados = (await resposta.json()) as RespostaValidacaoCnpj;
// { valido: true, cnpj: '12ABC34501DE35', formatado: '12.ABC.345/01DE-35',
//   formato: 'alfanumerico', raiz: '12ABC345', ordem: '01DE', matriz: false }

const lote = await fetch('https://www.cpf.dev.br/api/v1/cnpj/gerar?quantidade=5&formato=alfanumerico&formatado=true');
const { cnpjs } = (await lote.json()) as { cnpjs: string[] };

O as não valida nada em tempo de execução; se a fonte não for confiável, passe a resposta por um schema. Para testar casos à mão, o validador de CNPJ mostra o motivo de cada recusa, e o formatador de CNPJ aplica a máscara em lote.

Leia também