1062. Identidad de la lección
En esta lección convertirás una regla ajustable del juego en un pequeño contrato de datos que se pueda revisar. Definirás su estructura, establecerás valores predeterminados seguros, rechazarás entradas inválidas y campos desconocidos, distinguirás entre un campo ausente y uno que contenga explícitamente undefined, e identificarás qué código conserva la responsabilidad del comportamiento.
1063. Objetivo de aprendizaje
Al terminar, podrás definir y validar un conjunto de datos declarativos para que la configuración válida llegue al sistema de comportamiento, la inválida se rechace con una razón útil y los campos omitidos se traten de forma distinta a los valores proporcionados con un tipo incorrecto.
1064. Por qué importa
Un valor ajustable solo resulta útil si el juego puede cargarlo de manera consistente y otra persona puede revisar qué significa. Separar la configuración del comportamiento permite ajustar una regla sin reescribir el flujo que la ejecuta. La validación protege el tiempo de ejecución frente a datos incompletos, mal tipados, fuera de rango o escritos con errores.
La política sobre campos ausentes también forma parte del contrato. Si un campo es opcional, su valor predeterminado debe aplicarse normalmente cuando se omite. Si el campo se proporciona, su valor todavía debe validarse. Sin comprobar que la propiedad exista realmente en el objeto, { enabled: undefined } puede confundirse con una omisión y sustituirse silenciosamente por el valor predeterminado.
1065. Conocimientos previos
Debes poder:
- distinguir entre datos declarativos, comportamiento y estado de ejecución, como se trabajó en 3.4 L1 — Los datos no son comportamiento;
- describir en lenguaje sencillo la regla que quieres ajustar;
- leer la sintaxis básica de objetos y tipos;
- localizar de dónde obtiene actualmente el valor el comportamiento que lo consume.
Si el código existente no está claro, sigue primero el valor desde su origen hasta el comportamiento que lo utiliza. No empieces trasladando código ejecutable a un archivo de datos.
1066. Concepto central
Un conjunto de datos declarativos es un contrato, no solo una colección de números. El contrato responde seis preguntas:
- Estructura: ¿Qué campos existen y qué tipo tiene cada uno?
- Valores predeterminados: ¿Qué valor seguro se aplica cuando se omite un campo opcional?
- Presencia: ¿Un
undefinedproporcionado explícitamente cuenta como omisión o como valor inválido? - Validez: ¿Qué valores deben rechazarse y por qué?
- Campos desconocidos: ¿Qué ocurre si la entrada contiene una clave ajena al esquema aprobado?
- Responsabilidad: ¿Qué comportamiento lee los datos validados y aplica la regla del juego?
Para esta lección, elige una regla pequeña y ajustable. Un ejemplo genérico es una regla de recuperación:
const recoveryConfig = {
delaySeconds: 3,
amount: 10,
enabled: true
};
El objeto describe una configuración. No realiza la recuperación, no espera tres segundos ni modifica una entidad. Esas acciones siguen siendo comportamiento.
Un contrato útil es:
type RecoveryConfig = {
delaySeconds: number;
amount: number;
enabled: boolean;
};
El tipo describe el resultado ya validado, pero no demuestra que los datos externos o editados sean válidos. Una frontera de validación debe examinar los valores reales antes de devolver un RecoveryConfig.
Esta lección adopta dos políticas explícitas:
- Los campos desconocidos se rechazan.
- Un valor predeterminado solo se aplica cuando la propiedad correspondiente no existe en el objeto. Si el campo existe y contiene
undefined, se considera un valor proporcionado y la validación de tipo debe rechazarlo.
1067. Modelo mental
Usa el modelo DATOS → VALIDAR → CONSUMIR:
| Etapa | Responsabilidad | Pregunta guía |
|---|---|---|
| DATOS | Declarar valores ajustables y su estructura prevista | ¿Qué se puede ajustar? |
| VALIDAR | Detectar la presencia de campos, aplicar valores a las omisiones, validar lo proporcionado y rechazar claves desconocidas | ¿Está la entrada completa, bien tipada, permitida y sin ambigüedades? |
| CONSUMIR | Ejecutar el comportamiento con valores validados | ¿Qué hace el juego en ejecución con esta configuración? |
La dirección es de una sola vía. El sistema de comportamiento puede leer la configuración validada, pero esta no debe contener funciones ejecutables, flujo de control ni estado de ejecución oculto.
1068. Ejemplo concreto
Supón que el comportamiento contiene valores escritos directamente en el código:
const delaySeconds = 3;
const amount = 10;
Traslada únicamente los valores ajustables a un contrato de configuración:
type RecoveryConfig = {
delaySeconds: number;
amount: number;
enabled: boolean;
};
const defaultRecoveryConfig: RecoveryConfig = {
delaySeconds: 3,
amount: 10,
enabled: true
};
El validador puede rechazar campos desconocidos, distinguir una omisión de un undefined explícito y comprobar todos los valores resultantes:
function validateRecoveryConfig(
input: Record<string, unknown>
): RecoveryConfig {
const allowedKeys = new Set([
"delaySeconds",
"amount",
"enabled"
]);
for (const key of Object.keys(input)) {
if (!allowedKeys.has(key)) {
throw new Error(
`Unknown recovery configuration field: ${key}`
);
}
}
const delaySeconds = Object.hasOwn(input, "delaySeconds")
? input.delaySeconds
: defaultRecoveryConfig.delaySeconds;
const amount = Object.hasOwn(input, "amount")
? input.amount
: defaultRecoveryConfig.amount;
const enabled = Object.hasOwn(input, "enabled")
? input.enabled
: defaultRecoveryConfig.enabled;
if (
typeof delaySeconds !== "number" ||
!Number.isFinite(delaySeconds) ||
delaySeconds < 0
) {
throw new Error(
"delaySeconds must be a finite number greater than or equal to zero"
);
}
if (
typeof amount !== "number" ||
!Number.isFinite(amount) ||
amount <= 0
) {
throw new Error(
"amount must be a finite number greater than zero"
);
}
if (typeof enabled !== "boolean") {
throw new Error("enabled must be a boolean");
}
return { delaySeconds, amount, enabled };
}
Esta implementación produce resultados distintos:
{ amount: 12 }se acepta. Los campos ausentes reciben sus valores predeterminados.{ enabled: undefined }se rechaza. El campo está presente, por lo que el valor proporcionado debe ser booleano.{ amount: -5 }se rechaza porque el valor queda fuera del rango permitido.{ delaySecond: 4 }se rechaza porque la clave no pertenece al esquema.
Aquí es importante usar Object.hasOwn. Una comprobación como input.enabled === undefined trataría igual un campo omitido y { enabled: undefined }. Esa alternativa solo sería correcta si el contrato definiera deliberadamente undefined como omisión y existieran pruebas para esa política. Esta lección no adopta esa política.
El validador no decide cuándo ocurre la recuperación. Establece una configuración utilizable. El comportamiento existente sigue decidiendo cuándo comienza la recuperación y cómo aplica la cantidad configurada.
1069. Flujo de trabajo con IA
Usa la IA como asistente de implementación y como objeto de revisión, no como autoridad sobre el contrato.
- Escribe con tus propias palabras la regla, los campos, los valores aplicables a las omisiones, los casos inválidos y las políticas de presencia y de campos desconocidos.
- Pide a la IA que proponga un tipo y un validador a partir de esa especificación.
- Exige que distinga entre una propiedad ausente y una propiedad establecida explícitamente como
undefined. - Compara la propuesta con el modelo DATOS → VALIDAR → CONSUMIR.
- Rechaza funciones ejecutables, estado de ejecución, recortes no documentados, comportamiento dentro de la estructura de datos o gestión silenciosa de campos desconocidos.
- Inspecciona el cambio resultante y ejecuta las comprobaciones disponibles.
- Pide a la IA que explique cada rama de validación y contrasta la explicación con el código.
Una petición útil sería: «Propón un tipo de configuración y un validador para esta regla. Rechaza campos desconocidos. Aplica valores predeterminados solo a propiedades propias ausentes y rechaza los valores undefined proporcionados explícitamente. No implementes el comportamiento del juego. Enumera las suposiciones por separado».
1070. Errores comunes
Confundir los valores predeterminados con la validación
Combinar un objeto con valores predeterminados puede completar campos omitidos, pero no demuestra que los valores proporcionados tengan el tipo o el rango correctos. { amount: -5 } sigue pareciendo completo después de la combinación.
Comprobar únicamente si el valor es undefined
Una condición como input.enabled === undefined no distingue un campo omitido de { enabled: undefined }. Si el contrato establece que los valores predeterminados solo se aplican a omisiones, comprueba la presencia con Object.hasOwn y valida después el valor proporcionado.
Dejar pasar campos desconocidos
Una errata como delaySecond puede parecer aceptada aunque no tenga ningún efecto. Esta lección rechaza los campos desconocidos antes de construir el resultado validado.
Recortar silenciosamente valores inválidos
El recorte puede ocultar un error de autoría. Rechaza el valor salvo que el diseño defina expresamente el recorte como parte de la regla.
1071. Práctica guiada
Crea un conjunto de configuración validado para una regla ajustable del proyecto o para un ejemplo pequeño y aislado.
Paso 1: Expresa la regla
Elige una regla con al menos dos valores ajustables. Escribe una frase sobre lo que hace el juego en ejecución y otra sobre lo que los datos pueden controlar.
Paso 2: Define el contrato
Documenta:
- al menos dos campos tipados;
- el significado y la unidad de cada campo numérico;
- los valores aplicables cuando se omiten campos opcionales;
- las condiciones de validez;
- el rechazo de campos desconocidos;
- si un
undefinedexplícito cuenta como omisión o como entrada inválida.
En esta práctica, aplica valores únicamente a propiedades propias ausentes y rechaza los undefined proporcionados explícitamente.
Paso 3: Implementa la validación
Crea una frontera de validación y prueba al menos:
- una configuración completa y válida;
- una configuración parcial que reciba un valor para un campo omitido;
- un tipo incorrecto;
- un valor fuera de rango;
- un campo desconocido o escrito con una errata;
- un campo conocido proporcionado explícitamente como
undefined.
El último caso debe fallar según la política de esta lección. No lo sustituyas silenciosamente por un valor predeterminado.
Paso 4: Conecta el consumidor
Haz que el comportamiento elegido consuma la configuración validada en lugar de un valor ajustable escrito directamente en el código. No traslades el comportamiento a los datos.
Paso 5: Revisa la frontera
Responde:
- ¿Qué campos se pueden ajustar?
- ¿Qué omisiones reciben valores?
- ¿Qué ocurre si un campo conocido se establece explícitamente como
undefined? - ¿Qué entradas inválidas o desconocidas se rechazan?
- ¿Qué función conserva la responsabilidad del comportamiento del juego?
1072. Validación y evidencias
La evaluación práctica asociada exige:
- el contrato de configuración declarado;
- los significados, unidades y valores aplicables a omisiones;
- la frontera de validación;
- evidencias de casos válidos, omitidos, mal tipados, fuera de rango, desconocidos y con
undefinedexplícito; - un consumidor que use únicamente datos validados;
- una revisión del cambio que confirme que el estado de ejecución y el comportamiento ejecutable permanecen fuera del conjunto de datos.
1073. Ideas clave
- Los datos declarativos describen valores ajustables; no ejecutan la regla.
- Un tipo describe el resultado previsto, mientras que la validación comprueba la entrada real.
- Los valores predeterminados resuelven omisiones, no errores.
- La presencia de una propiedad y la igualdad de su valor son comprobaciones distintas.
- Los campos desconocidos necesitan una política explícita; en esta lección se rechazan.
- El sistema de comportamiento conserva la responsabilidad de aplicar la configuración validada.
1074. Siguiente lección
Continúa con 3.5 — Acoplamiento (lesson-s3-3-5-01-coupling-has-a-change-cost).
1075. Comprobación
Responde estas preguntas por tu cuenta antes de leer las respuestas.
¿Cuál es la responsabilidad principal de la etapa de validación?
Mostrar respuesta y explicación
Respuesta: Aplicar valores a las omisiones y rechazar configuraciones inválidas
Por qué: La validación establece una configuración completa y permitida. No ejecuta la regla ni conserva el estado de ejecución.
¿Por qué no basta con combinar una entrada con valores predeterminados para validarla?
Mostrar respuesta y explicación
Respuesta: Una combinación puede conservar tipos incorrectos o valores fuera de rango
Por qué: Los valores predeterminados resuelven omisiones, no garantizan la corrección. Los valores proporcionados todavía requieren comprobaciones de tipo y rango.
¿Qué elemento pertenece al consumidor del comportamiento y no al conjunto de datos declarativos?
Mostrar respuesta y explicación
Respuesta: La operación que espera y aplica la recuperación
Por qué: Esperar y modificar una entidad son comportamientos ejecutables. El retraso, el indicador de activación y la cantidad son valores de configuración.
¿Qué debes hacer si una propuesta de IA recorta silenciosamente un valor inválido, pero el diseño no define ese recorte?
Mostrar respuesta y explicación
Respuesta: Rechazar o modificar la propuesta y definir la política prevista
Por qué: El recorte silencioso puede ocultar un error de autoría o de diseño. La política debe decidirse de manera intencional.
Según la política de esta lección, ¿cómo debe tratarse { enabled: undefined }?
Mostrar respuesta y explicación
Respuesta: Rechazarlo porque la propiedad está presente con un valor no booleano
Por qué: Object.hasOwn(input, "enabled") identifica la propiedad como proporcionada. Por tanto, su valor debe superar la validación booleana en lugar de recibir el valor reservado para una omisión.