Kodokon kodokon.com

Sobrecargas, this tipado y varianza

Comprende la resolución de sobrecargas, el pseudoparámetro this y las reglas de varianza que rigen la asignabilidad de funciones.

10 min · 3 preguntas

Abrir esta lección en Kodokon

Una función sobrecargada expone una lista de firmas públicas seguida de una única firma de implementación. Un punto esencial de la especificación: esta última es invisible desde el exterior; solo las sobrecargas declaradas son invocables. La resolución recorre las firmas de arriba abajo y conserva la primera compatible: coloca siempre la firma más específica primero, de lo contrario nunca será seleccionada.

TYPESCRIPT
function parse(input: string): number;
function parse(input: string[]): number[];
function parse(input: string | string[]) {
  return Array.isArray(input)
    ? input.map((s) => s.length)
    : input.length;
}

const single = parse("abc");      // number
const many = parse(["a", "bb"]);  // number[]
Cada llamada se resuelve a una sobrecarga precisa.

TypeScript te permite declarar un pseudoparámetro this en primera posición. Se borra por completo del JavaScript emitido: es una restricción puramente estática sobre el contexto de llamada, comprobada cuando noImplicitThis está activado. Las utilidades ThisParameterType y OmitThisParameter permiten extraerlo o eliminarlo, lo cual es esencial para tipar funciones destinadas a usarse con call, apply o bind.

TYPESCRIPT
function greet(this: { name: string }, msg: string) {
  return `${msg}, ${this.name}`;
}

type Ctx = ThisParameterType<typeof greet>;
// { name: string }
type Fn = OmitThisParameter<typeof greet>;
// (msg: string) => string

const user = { name: "Ada", greet };
user.greet("Hello"); // "Hello, Ada"

// greet("Hi"); // error: missing this context
El parámetro this restringe el sitio de llamada, no el JS emitido.

La varianza describe cómo se propaga la asignabilidad a través de un constructor de tipos. Para las funciones, los tipos de retorno son covariantes y, con strictFunctionTypes, los parámetros son contravariantes: una función que acepta un tipo más amplio puede reemplazar a otra que acepta un tipo más estrecho, nunca al revés. Una excepción deliberada: los parámetros de los métodos declarados con sintaxis abreviada permanecen bivariantes, un compromiso histórico sin el cual Array<Dog> no sería asignable a Array<Animal>. Desde 4.7, puedes anotar la varianza esperada con in y out, por ejemplo interface Producer<out T>, y el compilador la verifica.

TYPESCRIPT
interface Animal { name: string }
interface Dog extends Animal { bark(): void }

declare let f: (a: Dog) => void;
declare let g: (a: Animal) => void;

f = g; // OK: g accepts a wider type, that is safe
// g = f; // error with strictFunctionTypes

interface Sink { accept(a: Animal): void }
interface DogSink { accept(a: Dog): void }
declare let s: Sink;
declare let d: DogSink;

d = s; s = d; // both compile: bivariance!
Propiedades de función estrictas, métodos bivariantes.

Prueba de conocimientos

Comprueba que has retenido los puntos clave de esta lección.

  1. Al llamar a una función sobrecargada, ¿qué firma elige el compilador?
    • La firma más general
    • La primera firma compatible en orden de declaración
    • La firma de implementación
    • La unión de todas las firmas
  2. ¿Qué le ocurre al parámetro this de una función en el JavaScript emitido?
    • Se convierte en un primer argumento real
    • Desaparece: es una anotación puramente estática
    • Se reemplaza por una llamada a bind
    • Provoca un error en tiempo de ejecución si falta
  3. Con strictFunctionTypes, ¿qué sintaxis se beneficia de la comprobación estricta de parámetros contravariante?
    • Los métodos con sintaxis abreviada, como m(a: X): void
    • Las propiedades con tipo de función, como m: (a: X) => void
    • Ambas sintaxis
    • Ninguna de ellas