Variância descreve como a subtipagem de um tipo composto se relaciona com a subtipagem de suas partes — ou seja, quando Container<Sub> é atribuível a Container<Super>?
Seja Dog um subtipo de Animal.
let dogs: Dog[] = [];
let animals: Animal[] = dogs; // ✅ Dog[] is assignable to Animal[]
Tipos de retorno e arrays são covariantes: se Dog ⊆ Animal, então Dog[] ⊆ Animal[]. Uma função que retorna Dog é utilizável onde se espera uma que retorna Animal.
type Handler<T> = (arg: T) => void;
let animalHandler: Handler<Animal> = (a) => {};
let dogHandler: Handler<Dog> = animalHandler; // ✅ (with strictFunctionTypes)
// a handler that accepts ANY Animal can safely handle a Dog
Parâmetros de função são contravariantes: Handler<Animal> é atribuível a Handler<Dog>, o inverso da relação de elementos. Isto é correto — algo que trata todos os animais certamente trata cães.
// Method parameters in TS are bivariant by default (a known unsound convenience)
interface Comparer<T> { compare(a: T): void; }
TypeScript verifica tipos de função autonomia contravariantes apenas sob strictFunctionTypes; parâmetros de método são intencionalmente bivariantes por ergonomia, o que tecnicamente é inseguro.
Variância explica por que certas atribuições são permitidas ou rejeitadas — por que Dog[] se ajusta a Animal[] mas um callback (d: Dog) => void nem sempre pode substituir (a: Animal) => void.
Compreender isso ajuda você a projetar APIs genéricas (por ex. posições somente leitura vs. escrita) e decodificar erros confusos "not assignable" envolvendo funções e genéricos.
Uma biblioteca de perguntas de entrevista de TI com respostas detalhadas — de Júnior a Sênior.
Doar