Reflexão e Introspecção em TypeScript: como o código se observa
\n\nImagine um programa capaz de analisar a si mesmo em tempo de execução. Ele pode descobrir quais tipos suas variáveis possuem, quais propriedades os objetos têm e até tomar decisões com base nessas informações. Parece mágica? Na verdade, isso é reflexão e introspecção — técnicas poderosas de metaprogramação que também estão disponíveis em TypeScript.
\n\nNeste artigo, vamos entender a diferença entre reflexão e introspecção, como usá-las em TypeScript e por que isso pode ser útil em projetos reais. Nada de teoria chata — apenas código vivo e exemplos práticos.
\n\nO que são Introspecção e Reflexão?
\n\nVamos logo esclarecer isso. Na programação, esses termos são frequentemente confundidos, mas há diferença:
\n\n- \n
- Introspecção — é a capacidade do programa de observar sua própria estrutura. Você pode descobrir quais propriedades um objeto tem, qual é o seu tipo, mas não pode alterá-las. \n
- Reflexão — é um nível mais avançado: o programa não apenas se observa, mas também altera seu comportamento em tempo de execução. Por exemplo, invoca métodos dinamicamente ou cria novas instâncias de classes. \n
Em JavaScript, no qual TypeScript é baseado, a reflexão é implementada através de Reflect e Proxy. Mas o TypeScript adiciona sua própria camada — a tipagem estática, que parcialmente "desaparece" na compilação. É aqui que a coisa fica interessante.
Introspecção de tipos: type guards e typeof
\n\nA maneira mais simples de olhar dentro do código é usar type guards. São construções que verificam o tipo de dado em tempo de execução:
\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction processValue(value: unknown) {\n if (isString(value)) {\n // TypeScript sabe que value é uma string\n console.log(value.toUpperCase());\n } else {\n console.log('Isso não é uma string');\n }\n}\n\nObserve o value is string — isso é um predicado de tipo. Ele informa ao TypeScript que, após a verificação, a variável pertence a um tipo específico. Isso é introspecção pura: observamos o tipo, mas não o alteramos.
Reflexão através da API Reflect
\n\nO objeto Reflect é um mecanismo embutido de reflexão em JavaScript. Ele permite realizar ações que antes eram feitas por métodos "mágicos", mas agora são unificadas. Aqui estão alguns exemplos:
const user = {\n name: 'Alexey',\n age: 30,\n};\n\n// Introspecção — obtém a lista de chaves\nconsole.log(Reflect.ownKeys(user)); // ['name', 'age']\n\n// Reflexão — lê dinamicamente uma propriedade\nconst propertyName = 'name';\nconsole.log(Reflect.get(user, propertyName)); // 'Alexey'\n\n// Reflexão — define dinamicamente uma propriedade\nReflect.set(user, 'name', 'Maria');\nconsole.log(user.name); // 'Maria'\n\nPor que Reflect é melhor que o velho user[prop]? Primeiro, ele retorna boolean para operações de definição, o que é útil para verificações. Segundo, ele funciona corretamente com proxies e descritores de propriedades. Para TypeScript, isso é especialmente importante, pois a reflexão permite contornar a tipagem estática quando realmente necessário.
Decoradores: reflexão turbinada
\n\nDecoradores são, talvez, o exemplo mais marcante de reflexão em TypeScript. Eles permitem anotar classes, métodos, propriedades e parâmetros, e então alterar seu comportamento. Desde o TypeScript 5.0, os decoradores se tornaram padrão (stage 3).
\n\nAqui está um exemplo de um decorador simples para registrar a chamada de um método:
\n\nfunction logMethod(\n target: any,\n propertyKey: string,\n descriptor: PropertyDescriptor\n) {\n const originalMethod = descriptor.valu