Dot Notation - Transformaciones en Propiedades Anidadas
¿Qué es Dot Notation?
Dot Notation es una característica de QuickModel que permite especificar transformaciones para propiedades anidadas directamente en el decorador padre, usando la sintaxis de punto (.).
En lugar de decorar cada modelo anidado, puedes especificar las transformaciones necesarias desde el modelo padre usando la notación de punto para acceder a propiedades profundas.
Sintaxis Básica
@Quick({
'nested.property': Type, // 1 nivel de profundidad
'parent.child.value': BigInt, // 2 niveles de profundidad
'deep.nested.path.value': Date, // N niveles de profundidad
})
class Model extends QModel<IModel> {
// ...
}Ejemplo Completo
Caso de Uso: E-Commerce Cart Item
// Backend interfaces
interface IProduct {
id: string;
name: string;
price: string; // BigInt serializado
createdAt: string; // Date serializada
}
interface ICartItem {
product: IProduct;
quantity: number;
addedAt: string; // Date serializada
}
// Opción 1: Sin Dot Notation (más limpio)
@Quick({
price: BigInt,
createdAt: Date,
})
class Product extends QModel<IProduct> {
declare id: string;
declare price: bigint;
declare createdAt: Date;
}
@Quick({
product: Product, // Product ya tiene sus transformaciones
addedAt: Date,
})
class CartItem extends QModel<ICartItem> {
declare product: Product;
declare quantity: number;
declare addedAt: Date;
}
// Opción 2: Con Dot Notation (todo en un lugar)
@Quick({
product: Product,
'product.price': BigInt, // ← Dot notation
'product.createdAt': Date, // ← Dot notation
addedAt: Date,
})
class CartItem extends QModel<ICartItem> {
declare product: Product;
declare quantity: number;
declare addedAt: Date;
}Cuándo Usar Dot Notation
✅ Buenos Casos de Uso
1. Cuando no controlas la clase anidada
// Usas una clase de terceros sin decoradores
import { ExternalModel } from 'external-library';
@Quick({
model: ExternalModel,
'model.timestamp': Date,
'model.amount': BigInt,
})
class MyModel extends QModel<IMyModel> {
declare model: ExternalModel;
}2. Para prototipos rápidos
// Desarrollo rápido, sin definir todas las clases
@Quick({
'address.zipCode': String,
'address.coordinates.lat': Number,
'address.coordinates.lng': Number,
})
class User extends QModel<IUser> {
address!: any;
}3. Transformaciones específicas en contexto
// El mismo Address se usa en diferentes contextos
@Quick({
address: Address,
'address.deliveryDate': Date, // Solo para pedidos, no para usuarios
})
class Order extends QModel<IOrder> {
address!: Address;
}❌ Cuándo NO Usar Dot Notation
1. Cuando la clase anidada se reutiliza mucho
// ❌ MAL: Repetir dot notation en cada lugar
@Quick({ product: Product, 'product.price': BigInt })
class CartItem extends QModel<ICartItem> {
/* ... */
}
@Quick({ product: Product, 'product.price': BigInt })
class OrderItem extends QModel<IOrderItem> {
/* ... */
}
@Quick({ product: Product, 'product.price': BigInt })
class WishlistItem extends QModel<IWishlistItem> {
/* ... */
}
// ✅ BIEN: Decorar Product una vez
@Quick({ price: BigInt })
class Product extends QModel<IProduct> {
price!: bigint;
}
@Quick({ product: Product })
class CartItem extends QModel<ICartItem> {
product!: Product;
}2. Cuando la lógica es compleja
// ❌ MAL: Dot notation para lógica compleja
@Quick({
'user.profile.settings.theme': (val) => validateTheme(val),
'user.profile.settings.language': (val) => normalizeLanguage(val),
'user.profile.settings.notifications.email': Boolean,
'user.profile.settings.notifications.push': Boolean,
})
class Account extends QModel<IAccount> {
/* ... */
}
// ✅ BIEN: Modelos separados con sus propias transformaciones
@Quick({
theme: (val) => validateTheme(val),
language: (val) => normalizeLanguage(val),
})
class Settings extends QModel<ISettings> {
/* ... */
}
@Quick({ settings: Settings })
class Profile extends QModel<IProfile> {
/* ... */
}
@Quick({ profile: Profile })
class User extends QModel<IUser> {
/* ... */
}Profundidad de Anidamiento
Dot notation soporta cualquier nivel de profundidad:
@Quick({
'level1.value': BigInt, // 1 nivel
'level1.level2.value': Date, // 2 niveles
'level1.level2.level3.value': RegExp, // 3 niveles
'level1.level2.level3.level4.value': Symbol, // 4 niveles
// ... N niveles soportados
})
class DeepModel extends QModel<IDeepModel> {
level1!: any;
}Combinación con Arrays
Dot notation funciona con arrays:
interface IUser {
posts: IPost[];
}
interface IPost {
id: string;
publishedAt: string;
metadata: [string, any][];
}
@Quick({
posts: [Post], // Array de Posts
'posts.publishedAt': Date, // Transforma publishedAt en cada Post
'posts.metadata': Map, // Transforma metadata en cada Post
})
class User extends QModel<IUser> {
posts!: Post[];
}Ventajas y Desventajas
✅ Ventajas
- Centralización: Todas las transformaciones en un solo lugar
- Flexibilidad: No necesitas modificar clases externas
- Rapidez: Ideal para prototipos y desarrollo ágil
- Contexto: Transformaciones específicas para cada uso
❌ Desventajas
- Duplicación: Si reutilizas el modelo, repites la dot notation
- Mantenibilidad: Más difícil de seguir con muchas propiedades
- Verboso: Más código que decorar la clase directamente
- Type Safety: TypeScript no valida paths anidados (strings)
Reglas de Prioridad
Cuando combinas dot notation con decoradores en el modelo anidado:
@Quick({ price: BigInt })
class Product extends QModel<IProduct> {
price!: bigint; // Transformación definida aquí
}
@Quick({
product: Product,
'product.price': String, // ← Esta transformación sobrescribe la de Product
})
class CartItem extends QModel<ICartItem> {
product!: Product;
}Regla: El decorador más cercano (padre) tiene prioridad sobre el decorador del modelo anidado.
Patrones Recomendados
Patrón 1: Clases Propias (Sin Dot Notation)
// ✅ RECOMENDADO: Para modelos que controlas
@Quick({ createdAt: Date })
class Address extends QModel<IAddress> {
createdAt!: Date;
}
@Quick({ address: Address })
class User extends QModel<IUser> {
address!: Address;
}Patrón 2: Clases Externas (Con Dot Notation)
// ✅ RECOMENDADO: Para clases que no controlas
import { ThirdPartyModel } from 'external';
@Quick({
model: ThirdPartyModel,
'model.timestamp': Date,
'model.value': BigInt,
})
class MyModel extends QModel<IMyModel> {
model!: ThirdPartyModel;
}Patrón 3: Híbrido
// ✅ VÁLIDO: Mezcla según necesidad
@Quick({ createdAt: Date })
class Product extends QModel<IProduct> {
price!: bigint; // Sin transformación por defecto
createdAt!: Date; // Siempre transformado
}
// En Cart: price se transforma a BigInt
@Quick({
items: [CartItem],
'items.product': Product,
'items.product.price': BigInt, // Contexto específico
})
class Cart extends QModel<ICart> {
items!: CartItem[];
}
// En Wishlist: price se deja como number
@Quick({
items: [WishlistItem],
'items.product': Product,
// 'items.product.price' NO se transforma aquí
})
class Wishlist extends QModel<IWishlist> {
items!: WishlistItem[];
}Resumen
Usa dot notation con moderación. Para código de producción, prefiere decorar cada clase. Para prototipos o clases externas, dot notation es perfecta.