Equivalencia desde Kotlin
Clases de valor
AproximadaTypeScript no tiene un tipo nominal nativo. La validación en tiempo de ejecución la realiza la fábrica con Number.isSafeInteger(remaining) && remaining > 0, que rechaza valores no enteros, no seguros, NaN e Infinity además de los no positivos. La distinción de EvaluationBudget frente a number existe solo en tiempo de compilación, mediante una marca (unique symbol) que el verificador de tipos usa para impedir mezclarlos. Esa marca se borra por completo al compilar: en tiempo de ejecución, EvaluationBudget es un number sin ningún metadato adicional.
Referencia Kotlin: Declarar un tipo nominal para un dato primitivo y validar su invariante al construirse.
- Mecanismo
- Tipo marcado (brand) con un
unique symboly una función fábrica que valida antes de construir. - Idea conservada
- El tipo distingue EvaluationBudget de number en tiempo de compilación, y la fábrica rechaza en tiempo de ejecución cualquier valor que no sea un entero seguro y positivo.
- Riesgo de diseño
- Hacer un cast directo (as EvaluationBudget) de un number a EvaluationBudget sin pasar por la fábrica, saltándose la validación de entero seguro y positivo.
- Recomendación
- Expón solo la fábrica y el tipo marcado; no exportes una forma de construir el valor sin validar.
- Convención del curso
- Nunca exportes una conversión directa number -> EvaluationBudget; solo la fábrica validada.
- Mecanismo del lenguaje
- Tipos marcados (brand) mediante un unique symbol no exportado.
Diferencias relevantes
- La marca es solo de tipos: se borra por completo al compilar, así que en tiempo de ejecución EvaluationBudget es indistinguible de un number.
- Nada impide construir el valor sin pasar por la fábrica salvo la disciplina del equipo, ya que un
as EvaluationBudgetexplícito puede saltarse la validación.
La fábrica es la única forma pública de obtener un EvaluationBudget.
@JvmInline
value class EvaluationBudget(val remaining: Int) {
init {
require(remaining > 0) { "El presupuesto debe ser positivo." }
}
}
val budget = EvaluationBudget(1000) // remaining = 1000declare const evaluationBudgetBrand: unique symbol;
/** A positive, safe-integer count of evaluations remaining in an optimization budget. */
export type EvaluationBudget = number & { readonly [evaluationBudgetBrand]: true };
export function evaluationBudget(remaining: number): EvaluationBudget {
if (!Number.isSafeInteger(remaining) || remaining <= 0) {
throw new RangeError(`El presupuesto debe ser un entero seguro positivo, se recibió ${remaining}.`);
}
return remaining as EvaluationBudget;
}