Verificações de tipo condicional para TypeScript: guia completo com exemplos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Biblioteca conditional-type-checks para TypeScript: testando tipos

TypeScript é uma linguagem poderosa com um sistema de tipos complexo, incluindo tipos condicionais, mapeamento, inferência de tipos e muito mais. No entanto, desenvolvedores frequentemente enfrentam o problema: como verificar se os tipos estão funcionando exatamente como planejado? Testes unitários comuns verificam valores em tempo de execução, mas não podem garantir a correção dos tipos em tempo de compilação. É aqui que a biblioteca conditional-type-checks entra em ação.

Esta biblioteca fornece um conjunto de utilitários para testes estáticos de tipos TypeScript. Ela permite escrever testes que verificam se um determinado tipo corresponde às expectativas: se é um subtipo de outro, se dois tipos são iguais, se um valor pode ser atribuído a outro, e assim por diante. Todas as verificações são realizadas em tempo de compilação, sem afetar o runtime.

Por que isso é necessário? Primeiro, para refatorar tipos complexos — você pode ter certeza de que as alterações não quebrarão o comportamento esperado. Segundo, para documentar intenções — testes de tipos servem como uma especificação viva. Terceiro, para bibliotecas que fornecem tipos complexos — os usuários terão confiança na sua correção. A biblioteca é especialmente útil ao trabalhar com tipos condicionais (extends, infer), template literals e tipos recursivos.

Instalação

A biblioteca é instalada via npm como dependência de desenvolvimento:

npm install --save-dev conditional-type-checks

Ou via yarn:

yarn add --dev conditional-type-checks

Nenhuma configuração adicional do TypeScript é necessária — a biblioteca consiste inteiramente de tipos e não gera código JavaScript.

Início rápido

Crie um arquivo type-tests.ts e escreva um teste simples:

import { IsExact } from 'conditional-type-checks';

type TestType = string;
type ExpectedType = string;

// Verifica se os tipos são exatamente iguais
type TestPass = IsExact<TestType, ExpectedType>; // true

// Se os tipos não coincidirem, haverá um erro de compilação
// type TestFail = IsExact<TestType, number>; // false

Para que o teste realmente verifique algo, use a construção expectType ou simplesmente uma atribuição com verificação:

import { assert } from 'conditional-type-checks';

type MyType = { name: string; age: number };

// Verifica se MyType é um objeto com os campos name e age
type Check = assert<IsExact<MyType, { name: string; age: number }>>; // OK

Se a verificação falhar, o TypeScript emitirá um erro de compilação com a mensagem Type 'false' does not satisfy the constraint 'true'.

Principais métodos e funções

A biblioteca fornece vários utilitários principais. Vamos analisar cada um em detalhes.

1. IsExact<T, U>

Assinatura: type IsExact<T, U> = ...

O que faz: Verifica se os tipos T e U são exatamente iguais. Retorna true se os tipos forem idênticos, caso contrário false. Leva em conta todas as nuances do TypeScript, incluindo readonly, optional e outros modificadores.

import { IsExact } from 'conditional-type-checks';

type A = { readonly x: number };
type B = { x: number };

type Test1 = IsExact<A, B>; // false — readonly é diferente
type Test2 = IsExact<string, string>; // true
type Test3 = IsExact<string | number, number | string>; // true — a ordem não importa
type Test4 = IsExact<{ a: string }, { a: string; b?: number }>; // false

2. IsAny<T>

Assinatura: type IsAny<T> = ...

O que faz: Verifica se T é do tipo any. Útil para garantir que tipos genéricos não estejam vazando any acidentalmente.

import { IsAny } from 'conditional-type-checks';

type Test1 = IsAny<any>; // true
type Test2 = IsAny<string>; // false
type Test3 = IsAny<unknown>; // false

3. IsNever<T>

Assinatura: type IsNever<T> = ...

O que faz: Verifica se T é do tipo never. Essencial para testar tipos condicionais que podem resultar em never.

import { IsNever } from 'conditional-type-checks';

type Test1 = IsNever<never>; // true
type Test2 = IsNever<string>; // false
type Test3 = IsNever<string | never>; // false — string | never é simplificado para string

4. IsUnknown<T>

Assinatura: type IsUnknown<T> = ...

O que faz: Verifica se T é do tipo unknown. Útil para validar tipos seguros.

import { IsUnknown } from 'conditional-type-checks';

type Test1 = IsUnknown<unknown>; // true
type Test2 = IsUnknown<any>; // false
type Test3 = IsUnknown<string>; // false

5. Has<T, U>

Assinatura: type Has<T, U> = ...

O que faz: Verifica se o tipo T possui todas as propriedades do tipo U. Útil para verificar se um tipo é uma extensão de outro.

import { Has } from 'conditional-type-checks';

type Base = { id: number; name: string };
type Extended = { id: number; name: string; age: number };

type Test1 = Has<Extended, Base>; // true — Extended tem todas as propriedades de Base
type Test2 = Has<Base, Extended>; // false — Base não tem age

6. IsSubtypeOf<T, U>

Assinatura: type IsSubtypeOf<T, U> = ...

O que faz: Verifica se T é um subtipo de U. Diferente de Has, esta verificação leva em conta a estrutura completa dos tipos.

import { IsSubtypeOf } from 'conditional-type-checks';

type Test1 = IsSubtypeOf<'a', string>; // true — 'a' é subtipo de string
type Test2 = IsSubtypeOf<string, 'a'>; // false
type Test3 = IsSubtypeOf<{ a: number }, object>; // true

Exemplos práticos

Vamos ver alguns exemplos mais complexos de uso da biblioteca em cenários reais.

Testando tipos condicionais

import { IsExact, assert } from 'conditional-type-checks';

// Tipo condicional que extrai o tipo de elemento de um array
type ExtractElement<T> = T extends (infer U)[] ? U : never;

// Testes
type Test1 = assert<IsExact<ExtractElement<number[]>, number>>; // OK
type Test2 = assert<IsExact<ExtractElement<string[]>, string>>; // OK
type Test3 = assert<IsExact<ExtractElement<number>, never>>; // OK

Testando tipos de funções

import { IsExact, assert } from 'conditional-type-checks';

type MyFunction = (x: number, y: string) => boolean;

type Test1 = assert<IsExact<Parameters<MyFunction>, [number, string]>>; // OK
type Test2 = assert<IsExact<ReturnType<MyFunction>, boolean>>; // OK

Testando tipos com genéricos

import { IsExact, assert } from 'conditional-type-checks';

type Wrapper<T> = { value: T };

type Test1 = assert<IsExact<Wrapper<number>, { value: number }>>; // OK
type Test2 = assert<IsExact<Wrapper<string>, { value: string }>>; // OK

Conclusão

A biblioteca conditional-type-checks é uma ferramenta indispensável para qualquer desenvolvedor TypeScript que trabalhe com tipos complexos. Ela permite escrever testes de tipo que são executados em tempo de compilação, garantindo que seu sistema de tipos se comporte conforme o esperado. Com utilitários como IsExact, IsAny, IsNever, Has e IsSubtypeOf, você pode criar testes robustos e confiáveis para seus tipos.

Incorpore esta biblioteca em seu fluxo de desenvolvimento e tenha mais confiança na segurança dos tipos do seu código TypeScript!

Recommendations