Como tuve un problema en el que mi constructor se volvió bastante grande y comenzó a ser un problema mantenerlo. Creé esta solución como un comienzo del refactor, sin embargo, esto todavía parecía voluminoso. Y era fácil cometer un error.
constructor(data: Partial<BusinessConfiguration>) { if(!data) return; this.logoId = data.logoId; this.positiveLogoId = data.positiveLogoId; this.negativeLogoId = data.negativeLogoId; this.faviconId = data.faviconId; this.primaryColor = data.primaryColor; this.windowTabName = data.windowTabName; this.shouldShowGDPR = data.shouldShowGDPR; this.allowsTermination = data.allowsTermination; this.styleConfiguration = data.styleConfiguration; this.shouldUseCustomTitle = data.shouldUseCustomTitle; this.processNotesStickers = data.processNotesStickers; this.terminationProcesses = data.terminationProcesses; this.shouldUseCustomHeader =data.shouldUseCustomHeader; this.completeDelete = data.completeDelete this.pingConfiguration = data.pingConfiguration; this.passwordConfiguration = data.passwordConfiguration; this.shouldAllowRandomRepeat = data.shouldAllowRandomRepeat; this.shouldRedirectImmediately = data.shouldRedirectImmediately; }Así que creé esto, con esta lógica y una buena validación en mis rutas, nunca más tendré que actualizar mi constructor. Cualquier cosa que enviemos, esas propiedades serán asignadas. Sin embargo, estoy un poco descontento con el hecho de que soy un poco forzado en esto. Quiero verificar si el elemento de los datos existe como una propiedad de la clase BusinessConfiguration.
constructor(data: Partial<BusinessConfiguration>) { if(!data) return; Object.keys(data).forEach(element => { this[element] = data[element] }); }Tuve este problema en varias ocasiones y tal vez este no sea el mejor ejemplo, ya que tengo un tipo de datos. Pero si los datos ingresan a este constructor como tipo de cualquiera, pueden ocurrir errores y cosas no deseadas.
Entonces, la conclusión es cómo acceder a las claves de propiedad de clase antes de la inicialización del objeto.
Tuve la idea de crear algún tipo de estructura de datos que mantuviera todas las claves de propiedad de clase, pero espero que haya algo más limpio. Y si no lo hay, ¿cómo haría para que eso fuera lo más limpio posible? Las ideas son bienvenidas. Gracias de antemano :)
En lugar de definir campos en el constructor, puede usar campos de clase pública :
class Business { // Defines a property but without a value (undefined). // You can add TypeScript types to these. logoId; positiveLogoId; negativeLogoId; // ... }A continuación, en el constructor puede incluir un bucle similar al que tiene, pero con un filtro adicional:
class Business { logoId; positiveLogoId; negativeLogoId; constructor(data) { Object .keys(data) .filter(key => this.hasOwnProperty(key)) .forEach((element) => { this[element] = data[element]; }); } } Una sintaxis diferente, usando el moderno hasOwn y for...of :
class Business { logoId; positiveLogoId; negativeLogoId; constructor(data) { for (const [key, value] of Object.entries(data)) { if (Object.hasOwn(this, key)) { this[key] = value; } } } }Usando cualquiera de los anteriores, ahora solo puede establecer propiedades que existen:
class Business { logoId; positiveLogoId; negativeLogoId; constructor(data) { for (const [key, value] of Object.entries(data)) { if (Object.hasOwn(this, key)) { this[key] = value; } } } } const b = new Business({ logoId: 5, positiveLogoId: 10, foo: 1, }); console.log( 'Does the object contain "logoId"?', Object.keys(b).includes('logoId'), '(should be true)' ); console.log( 'Does the object contain "foo"?', Object.keys(b).includes('foo'), '(should be false)' );La mejor respuesta que se me ocurrió es tener una variable estática en la clase donde almacenaría los nombres de las propiedades, pero luego esta matriz debe actualizarse, lo cual no es muy diferente a actualizar el constructor, pero obtengo un beneficio adicional. donde puedo acceder a las claves de esta clase para este tipo de operaciones si lo necesito.
class Business { private static keys = ['logoId', 'positiveLogoId', 'negativeLogoId'] logoId; positiveLogoId; negativeLogoId; constructor(data: Partial<Business>) { Object.keys(data).forEach((element) => { if (Business.keys.includes(element)) this[element] = data[element]; }); } } const obj = new Business({ logoId: 5, positiveLogoId: '10', foo: 1 }as any) console.log(obj) // Business { logoId: 5, positiveLogoId: '10'}Podría introducir un asistente que tome un objeto y una matriz de nombres de propiedades. Luego devuelve un nuevo objeto que solo contiene las propiedades dadas (si están presentes).
function objSlice(obj, props = []) { return Object.fromEntries( props .filter(prop => prop in obj) .map(prop => [prop, obj[prop]]) ); }Ejemplo de uso:
const data = { a: 1, b: 2, c: 3 }; objSlice(data, ["c", "a", "d"]); //=> { c: 3, a: 1 } Puede usar esto en combinación con Object.assign() y un atributo propNames estático que contiene nombres de propiedad permitidos para filtrar muy fácilmente las propiedades no deseadas.
Object.assign(this, objSlice(data, this.constructor.propNames));Aquí hay un ejemplo:
class Book { static propNames = ["title", "author", "releaseYear"]; constructor(data = {}) { Object.assign(this, objSlice(data, this.constructor.propNames)); } toString() { return `${this.title} (${this.releaseYear}) - ${this.author}`; } } const helloWorld = new Book({ title: "Hello World!", author: "John Doe", releaseYear: 1337, // undesired data property toString() { return "conflicting with Book method"; }, }); console.log(`The book "${helloWorld}" was a good read.`); // helper function objSlice(obj, props = []) { return Object.fromEntries( props .filter(prop => prop in obj) .map(prop => [prop, obj[prop]]) ); }