this, call signatures y construct signatures en TypeScript | Nicolás GarzónTypeScript
function describe(
this: Product,
prefix: string,
): string {
return `${prefix}: ${this.name}`;
}
this aparece en el tipo, pero no es un argumento runtime adicional.
TypeScript
describe.call(product, "Product");
Con noImplicitThis, TypeScript detecta contextos donde this no tiene un tipo claro.
TypeScript
const object = {
value: 10,
getValue() {
return this.value;
},
};
El método recibe contextual typing desde el objeto.
ThisType<T> puede proporcionar tipo contextual de this dentro de object literals usados como DSL.
TypeScript
type ObjectDescriptor<D, M> = {
data: D;
methods: M & ThisType<D & M>;
};
No genera nada en runtime; funciona como marcador para el checker.
TypeScript
type Method = (
this: ProductService,
id: string,
) => Product;
type BoundMethod = OmitThisParameter<Method>;
Es útil cuando bind produce una función que ya no necesita receptor explícito.
TypeScript
type Receiver = ThisParameterType<Method>;
TypeScript
type Counter = {
(step?: number): number;
reset(): void;
};
TypeScript
function createCounter(): Counter {
let value = 0;
const counter = (step = 1) => {
value += step;
return value;
};
counter.reset = () => {
value = 0;
};
return counter;
}
TypeScript
type ProductConstructor = new (
id: string,
name: string,
) => Product;
TypeScript
function createInstance(
Constructor: ProductConstructor,
): Product {
return new Constructor("p1", "Keyboard");
}
TypeScript
type AbstractConstructor<T> = abstract new (
...args: never[]
) => T;
Permite aceptar clases abstractas en utilidades que trabajan con herencia, aunque no puedan instanciarse directamente.
Algunas APIs históricas pueden invocarse con o sin new.
TypeScript
type DateLike = {
(): string;
new (): Date;
};
No diseñes una API híbrida nueva sin una razón clara; es más difícil de entender y tipar.
TypeScript
class ProductService {
static version = 1;
constructor(
readonly repository: ProductRepository,
) {}
}
type ProductServiceInstance = ProductService;
type ProductServiceClass = typeof ProductService;
ProductService en posición de tipo representa la instancia.
typeof ProductService representa el constructor y miembros estáticos.
TypeScript
type Args = ConstructorParameters<
typeof ProductService
>;
type Instance = InstanceType<
typeof ProductService
>;
TypeScript
interface UIElement {
addClickListener(
handler: (this: void, event: Event) => void,
): void;
}
this: void comunica que la callback no debe depender de un receptor proporcionado por la API.
Una arrow no declara su propio this.
TypeScript
class Controller {
value = 10;
handler = () => {
console.log(this.value);
};
}
El tipo no cambia el costo runtime: cada instancia crea su propia arrow field.
TypeScript
type AdapterConstructor<TAdapter> = new (
configuration: AdapterConfiguration,
) => TAdapter;
function createAdapter<TAdapter>(
Constructor: AdapterConstructor<TAdapter>,
configuration: AdapterConfiguration,
): TAdapter {
return new Constructor(configuration);
}
- Creer que
this en la firma se pasa como argumento normal.
- Confundir el tipo de instancia con
typeof Class.
- Tipar un constructor como
() => T.
- Usar una arrow esperando que
call o bind cambien su this.
- Crear APIs callable y constructible sin necesidad.
- Utilizar
any en construct signatures.
- Asumir que
ThisType existe en runtime.
- El parámetro
this solo describe el receptor.
- Una call signature describe invocación normal.
- Una construct signature describe
new.
- El nombre de una clase como tipo representa instancias.
typeof Class representa el lado estático y constructor.
- Arrows capturan
this de JavaScript.
- Utility types pueden extraer receptor, parámetros e instancia.
¿Por qué ProductService y typeof ProductService representan tipos distintos?
Respuesta
ProductService representa las instancias creadas; typeof ProductService representa el valor constructor, sus miembros estáticos y su firma new.
Funciones asíncronas y Promises tipadas modela resultados futuros sin cambiar el comportamiento del Event Loop.