Supongamos que tengo BehaviorSubject con interfaz de usuario:
interface User { firstName: string; lastName: string; } let user: User = { firstName: 'Cuong', lastName: 'Le', }; let bs = new BehaviorSubject<User>(user);
Hay dos suscripciones, sub1
intentó cambiar el nombre. Sub2
se suscribe más tarde y el objeto de usuario también ha cambiado de nombre, ya que sub1
lo cambió antes:
let sub1 = bs.subscribe((u) => { u.firstName = 'An'; console.log(u); }); let sub2 = bs.subscribe((u) => { console.log(u); });
es difícil depurar cuando este caso ocurre en una gran aplicación Angular. ¿Cómo hacemos que el valor sea inmutable al suscribirse?
Estoy buscando una solución inmutable profunda para evitar que el código en otro lugar cambie los datos en lugar de la sombra
Usar esta respuesta y envolver su tipo en este tipo de utilidad inmutable debería resolver su problema. A diferencia de la palabra clave de solo lectura o el tipo de utilidad de solo lectura, la interfaz inmutable en esta respuesta hará que todas las propiedades sean de solo lectura de forma recursiva.
interface User { firstName: string; lastName: string; address: { street: string; } } let user: User = { firstName: 'Cuong', lastName: 'Le', address: { street: '123 Sesame St.' } }; let bs = new BehaviorSubject<Immutable<User>>(user); let sub1 = bs.subscribe((u) => { u.address.street = '456'; //<-- Cannot assign to 'street' because it is a read-only property. console.log(u); });
Puede usar el tipo de utilidad Readonly<T>
para marcar el tipo en el BehaviorSubject como Readonly<User>
en lugar de User
:
let bs = new BehaviorSubject<Readonly<User>>(user);
Ahora, TS le avisará cuando intente modificar:
No se puede asignar a 'firstName' porque es una propiedad de solo lectura. (2540)
Este es un problema que he tenido que resolver varias veces. Siempre que pase objetos simples no circulares a través del sujeto, esta solución debería funcionar.
El único requisito es que utilice la biblioteca lodash , que admite una copia profunda muy rápida (sin copias de referencia). He buscado por todas partes y todavía tengo que encontrar uno que sea más rápido.
let bs = (new BehaviorSubject<User>(user)).pipe( map((userData) => _.cloneDeep(userData)) );
Lo he usado en objetos de estado bastante grandes y siempre me ha impresionado la rapidez con la que puede realizar copias. La implicación aquí es que cualquier emisión que salga del sujeto es una copia del original, por lo que los cambios ocurren de forma aislada y nunca podrán volver a la fuente.