1657120860
El signo de exclamación !
se conoce como el operador de aserción no nulo en TypeScript. Usaremos estos términos indistintamente en este artículo. Pero, ¿qué hace este operador?
El operador de aserción no nulo le dice al compilador de TypeScript que un valor escrito como opcional no puede ser null
o undefined
. Por ejemplo, si definimos una variable como posiblemente una cadena o indefinida, el !
operador le dice al compilador que ignore la posibilidad de que no esté definida.
Digamos que una variable se define como posiblemente nula o indefinida, así:
let x: string | undefined
O, digamos que una función se define para aceptar un argumento opcional, así:
function printString (str ?: string) { … }
En estos casos, si intentamos hacer referencia a esa variable como un tipo definido, entonces el compilador de TypeScript nos daría un mensaje de error, como el siguiente:
Object is possibly 'undefined'. ts(2532)
Podemos usar el operador de aserción no nulo para decirle al compilador explícitamente que esta variable tiene un valor y no es null
o undefined
. Revisemos algunos ejemplos para comprender mejor el signo de exclamación en TypeScript.
string | null
para una función que aceptastring
Digamos que definimos una variable word
con el tipo como string | null
. Esto significa que a lo largo de nuestro código, word
puede contener un string
valor o un null
valor.
Si intentamos usar una función solo disponible para string
tipos en word
, TypeScript la rechazará porque existe una posibilidad en nuestro código que word
contiene un null
tipo de valor:
let word : string | null = null
const num = 1
if (num) {
word = "Hello World!"
}
console.log(word.toLowerCase()) // Error: Object is possibly 'null'.ts(2531)
Usando el !
operador de aserción no nulo, podemos decirle a TypeScript que estamos seguros de word
que nunca lo será null
(o undefined
), por lo que puede aplicarle con confianza funciones de cadena:
let word : string | null = null
const num = 1
if (num) {
word = "Hello World!"
}
console.log(word!.toLowerCase())
Con esta pequeña adición, el compilador ya no cree que exista la posibilidad de que word
sea nulo.
En otro ejemplo, digamos que creamos una función printName
que acepta un argumento opcional personName
.
Tenga en cuenta que definir un argumento de función como uso opcional ?:
es lo mismo que definir el tipo como posiblemente indefinido. Por ejemplo, arg?: string
es lo mismo que arg: string | undefined
.
Si intentamos reasignar ese argumento opcional personName
a otra variable de tipo string
, ocurriría lo siguiente:
function printName(personName?: string) {
const fullName: string = personName
/**
* Error: Type 'string | undefined' is not assignable to type 'string'.
* Type 'undefined' is not assignable to type 'string'.
*/
console.log(`The name is ${fullName}`)
}
Podemos arreglar los errores de TypeScript arrojados en nuestro fragmento anterior usando el !
operador:
function printName(personName?: string) {
const fullName: string = personName!
console.log(`The name is ${fullName}`)
}
Ahora, el compilador entiende que personName no puede ser nulo o indefinido, lo que lo hace asignable a type string
.
En nuestro ejemplo final, definiremos un tipo Person
y una función printName
que acepta un argumento opcional de tipo Person
. Veamos qué sucede si intentamos utilizar printName
para imprimir el atributo de nombre de Person
:
interface Person {
name: string
age: number
}
function printName(person?: Person) {
console.log(`The name is ${person.name}`) // Error: Object is possibly 'undefined'. ts(2532)
}
Arreglemos este error de TypeScript usando nuestro !
operador:
interface Person {
name: string
age: number
}
function printName(person?: Person) {
console.log(`The name is ${person!.name}`)
}
Tenga en cuenta que TypeScript tiene una alternativa para hacer referencia a atributos y funciones en objetos que pueden ser nulos o indefinidos, llamada operador de encadenamiento opcional ?.
. Por ejemplo, person?.name
o word?.toString()
regresará undefined
si la variable no está definida o es nula.
Sin embargo, el operador de encadenamiento opcional ?.
no puede resolver los errores de TypeScript en nuestro segundo ejemplo, en el que tratamos de asignar el valor de un tipo de variable string | undefined
a un tipo de variable string
. Obtenga más información sobre el encadenamiento opcional en la última sección de este artículo .
Como hemos visto en nuestros ejemplos, el !
operador es muy útil cuando deseamos que TypeScript trate nuestra variable como un tipo sólido. Esto evita que tengamos que manejar casos nulos o indefinidos cuando estamos seguros de que no existe tal caso.
Ahora que hemos visto algunos ejemplos para comprender mejor el signo de exclamación de TypeScript, veamos algunos casos de uso populares para este operador.
Imaginemos que tenemos una matriz de objetos y queremos elegir un objeto con un valor de atributo particular, así:
interface Person {
name: string
age: number
sex: string
}
const people: Person[] = [
{
name: 'Gran',
age: 70,
sex: 'female'
},
{
name: 'Papa',
age: 72,
sex: 'male'
},
{
name: 'Mom',
age: 35,
sex: 'female'
},
{
name: 'Dad',
age: 38,
sex: 'male'
}
]
const femalePerson = people.find(p => p.sex === 'female')
En nuestro fragmento anterior, TypeScript definirá el tipo de femalePerson
como Person | undefined
porque es posible que people.find
no produzca ningún resultado, en otras palabras, que no estará definido.
Sin embargo, si femalePerson
tiene el tipo Person | undefined
, no podremos pasarlo como argumento a una función que espera el tipo Person
.
Cuando realizamos búsquedas en estas matrices, a menudo confiamos en que tienen valores definidos y, por lo tanto, no creemos que exista ningún caso indefinido. Nuestro !
operador puede salvarnos del manejo adicional, o innecesario, de casos nulos o indefinidos.
Agregue el operador de aserción no nulo, así:
const femalePerson = people.find(p => p.sex === 'female')!
Esto haría femalePerson
tener el tipo Person
.
Las referencias de React se utilizan para acceder a los nodos HTML DOM renderizados o a los elementos de React. Las referencias se crean usando React.createRef<HTMLDivElement>()
y luego se adjuntan al elemento usando el ref
atributo.
Para usar React refs, accedemos al atributo actual, ref.current
. Hasta que el elemento se represente, ref.current
podría ser null
, por lo que tiene el siguiente tipo:
HTMLDivElement | null
Para adjuntar un evento a ref.current
, primero tendríamos que manejar los null
valores posibles. Aquí hay un ejemplo:
import React from 'react'
const ToggleDisplay = () => {
const displayRef = React.createRef<HTMLDivElement>()
const toggleDisplay = () => {
if (displayRef.current) {
displayRef.current.toggleAttribute('hidden')
}
}
return (
<div>
<div class="display-panel" ref="displayRef">
<p> some content </p>
</div>
<button onClick={toggleDisplay}>Toggle Content</button>
<div>
)
}
En el fragmento anterior, tuvimos que manejar una verificación de tipo para displayRef.current
usar una if
declaración antes de llamar a la toggleAttribute
función.
En la mayoría de los casos, estamos seguros de que si onClick
se activa el evento del botón, nuestros elementos ya están representados. Por lo tanto, no hay necesidad de un control. Esta verificación innecesaria se puede eliminar usando el !
operador, así:
const displayRef = React.createRef<HTMLDivElement>()
const toggleDisplay = () => displayRef.current!.toggleAttribute('hidden')
return (
<div>
...
El !
operador no cambia el comportamiento de tiempo de ejecución de su código. Si el valor que ha afirmado no es null
o undefined
resulta ser null
o undefined
, se producirá un error e interrumpirá la ejecución de su código.
Recuerde, la diferencia entre TypeScript y JavaScript es la aserción de tipos. En JavaScript no necesitamos ni usamos el !
operador porque no hay tipo estricto.
Se puede crear una instancia de una variable de JavaScript string
y cambiarla a object
, null
o number
durante la ejecución del código. Esto deja en manos del desarrollador el manejo de los diferentes casos.
El uso del !
operador elimina el "superpoder" de TypeScript de evitar errores de tipo en tiempo de ejecución. Por lo tanto, no es la mejor práctica usar el !
operador.
Puede usar encadenamientos opcionales o predicados de tipos como alternativas a las aserciones no nulas.
El encadenamiento opcional es una forma abreviada de TypeScript que nos permite manejar fácilmente los casos en los que la variable está definida o no. Cuando la variable no está definida o es nula, el valor al que se hace referencia por defecto es value undefined
. He aquí un ejemplo de encadenamiento opcional:
interface Person {
name: string
age: number
sex: string
}
function printName(person?: Person): void {
console.log('The name of this person is', person?.name)
}
En nuestro ejemplo anterior, si person
no está definido, nuestra salida de impresión sería la siguiente:
'The name of this person is undefined'
El uso de predicados de tipo en TypeScript se realiza definiendo una función que realiza una prueba booleana y devuelve un predicado de tipo en el formulario arg is Type
. Aquí hay un ejemplo:
interface Person {
name: string
age: number
sex: string
}
function validatePerson(person?: Person) person is Person {
return !!person
}
Usando este tipo de predicado, primero podemos validar el objeto antes de realizar más operaciones, así:
function printName(person?: Person) {
if (!validatePerson(person)) {
console.log('Person is invalid')
return
}
console.log(`The name is ${person.name}`)
}
El poder de TypeScript sobre JavaScript es la seguridad de tipos que proporciona a nuestro código . Sin embargo, es posible que a veces queramos deshabilitar las comprobaciones estrictas de tipos de TypeScript, por ejemplo, en aras de la flexibilidad o la compatibilidad con versiones anteriores. En tales casos, podemos usar el operador de aserción no nulo.
Si bien es una característica útil, lo animo a explorar métodos de aserción de tipos más seguros. Puede ir un paso más allá para evitar el uso de esta operación en su proyecto y con su equipo agregando el typescript-eslint
paquete a su proyecto y aplicando la no-non-null-assertion
regla de lint .
Fuente: https://blog.logrocket.com/understanding-exclamation-mark-typescript/
1654588030
TypeScript Deep Dive
I've been looking at the issues that turn up commonly when people start using TypeScript. This is based on the lessons from Stack Overflow / DefinitelyTyped and general engagement with the TypeScript community. You can follow for updates and don't forget to ★ on GitHub 🌹
If you are here to read the book online get started.
Book is completely free so you can copy paste whatever you want without requiring permission. If you have a translation you want me to link here. Send a PR.
You can also download one of the Epub, Mobi, or PDF formats from the actions tab by clicking on the latest build run. You will find the files in the artifacts section.
All the amazing contributors 🌹
Share URL: https://basarat.gitbook.io/typescript/
Author: Basarat
Source Code: https://github.com/basarat/typescript-book/
License: View license
1657120860
El signo de exclamación !
se conoce como el operador de aserción no nulo en TypeScript. Usaremos estos términos indistintamente en este artículo. Pero, ¿qué hace este operador?
El operador de aserción no nulo le dice al compilador de TypeScript que un valor escrito como opcional no puede ser null
o undefined
. Por ejemplo, si definimos una variable como posiblemente una cadena o indefinida, el !
operador le dice al compilador que ignore la posibilidad de que no esté definida.
Digamos que una variable se define como posiblemente nula o indefinida, así:
let x: string | undefined
O, digamos que una función se define para aceptar un argumento opcional, así:
function printString (str ?: string) { … }
En estos casos, si intentamos hacer referencia a esa variable como un tipo definido, entonces el compilador de TypeScript nos daría un mensaje de error, como el siguiente:
Object is possibly 'undefined'. ts(2532)
Podemos usar el operador de aserción no nulo para decirle al compilador explícitamente que esta variable tiene un valor y no es null
o undefined
. Revisemos algunos ejemplos para comprender mejor el signo de exclamación en TypeScript.
string | null
para una función que aceptastring
Digamos que definimos una variable word
con el tipo como string | null
. Esto significa que a lo largo de nuestro código, word
puede contener un string
valor o un null
valor.
Si intentamos usar una función solo disponible para string
tipos en word
, TypeScript la rechazará porque existe una posibilidad en nuestro código que word
contiene un null
tipo de valor:
let word : string | null = null
const num = 1
if (num) {
word = "Hello World!"
}
console.log(word.toLowerCase()) // Error: Object is possibly 'null'.ts(2531)
Usando el !
operador de aserción no nulo, podemos decirle a TypeScript que estamos seguros de word
que nunca lo será null
(o undefined
), por lo que puede aplicarle con confianza funciones de cadena:
let word : string | null = null
const num = 1
if (num) {
word = "Hello World!"
}
console.log(word!.toLowerCase())
Con esta pequeña adición, el compilador ya no cree que exista la posibilidad de que word
sea nulo.
En otro ejemplo, digamos que creamos una función printName
que acepta un argumento opcional personName
.
Tenga en cuenta que definir un argumento de función como uso opcional ?:
es lo mismo que definir el tipo como posiblemente indefinido. Por ejemplo, arg?: string
es lo mismo que arg: string | undefined
.
Si intentamos reasignar ese argumento opcional personName
a otra variable de tipo string
, ocurriría lo siguiente:
function printName(personName?: string) {
const fullName: string = personName
/**
* Error: Type 'string | undefined' is not assignable to type 'string'.
* Type 'undefined' is not assignable to type 'string'.
*/
console.log(`The name is ${fullName}`)
}
Podemos arreglar los errores de TypeScript arrojados en nuestro fragmento anterior usando el !
operador:
function printName(personName?: string) {
const fullName: string = personName!
console.log(`The name is ${fullName}`)
}
Ahora, el compilador entiende que personName no puede ser nulo o indefinido, lo que lo hace asignable a type string
.
En nuestro ejemplo final, definiremos un tipo Person
y una función printName
que acepta un argumento opcional de tipo Person
. Veamos qué sucede si intentamos utilizar printName
para imprimir el atributo de nombre de Person
:
interface Person {
name: string
age: number
}
function printName(person?: Person) {
console.log(`The name is ${person.name}`) // Error: Object is possibly 'undefined'. ts(2532)
}
Arreglemos este error de TypeScript usando nuestro !
operador:
interface Person {
name: string
age: number
}
function printName(person?: Person) {
console.log(`The name is ${person!.name}`)
}
Tenga en cuenta que TypeScript tiene una alternativa para hacer referencia a atributos y funciones en objetos que pueden ser nulos o indefinidos, llamada operador de encadenamiento opcional ?.
. Por ejemplo, person?.name
o word?.toString()
regresará undefined
si la variable no está definida o es nula.
Sin embargo, el operador de encadenamiento opcional ?.
no puede resolver los errores de TypeScript en nuestro segundo ejemplo, en el que tratamos de asignar el valor de un tipo de variable string | undefined
a un tipo de variable string
. Obtenga más información sobre el encadenamiento opcional en la última sección de este artículo .
Como hemos visto en nuestros ejemplos, el !
operador es muy útil cuando deseamos que TypeScript trate nuestra variable como un tipo sólido. Esto evita que tengamos que manejar casos nulos o indefinidos cuando estamos seguros de que no existe tal caso.
Ahora que hemos visto algunos ejemplos para comprender mejor el signo de exclamación de TypeScript, veamos algunos casos de uso populares para este operador.
Imaginemos que tenemos una matriz de objetos y queremos elegir un objeto con un valor de atributo particular, así:
interface Person {
name: string
age: number
sex: string
}
const people: Person[] = [
{
name: 'Gran',
age: 70,
sex: 'female'
},
{
name: 'Papa',
age: 72,
sex: 'male'
},
{
name: 'Mom',
age: 35,
sex: 'female'
},
{
name: 'Dad',
age: 38,
sex: 'male'
}
]
const femalePerson = people.find(p => p.sex === 'female')
En nuestro fragmento anterior, TypeScript definirá el tipo de femalePerson
como Person | undefined
porque es posible que people.find
no produzca ningún resultado, en otras palabras, que no estará definido.
Sin embargo, si femalePerson
tiene el tipo Person | undefined
, no podremos pasarlo como argumento a una función que espera el tipo Person
.
Cuando realizamos búsquedas en estas matrices, a menudo confiamos en que tienen valores definidos y, por lo tanto, no creemos que exista ningún caso indefinido. Nuestro !
operador puede salvarnos del manejo adicional, o innecesario, de casos nulos o indefinidos.
Agregue el operador de aserción no nulo, así:
const femalePerson = people.find(p => p.sex === 'female')!
Esto haría femalePerson
tener el tipo Person
.
Las referencias de React se utilizan para acceder a los nodos HTML DOM renderizados o a los elementos de React. Las referencias se crean usando React.createRef<HTMLDivElement>()
y luego se adjuntan al elemento usando el ref
atributo.
Para usar React refs, accedemos al atributo actual, ref.current
. Hasta que el elemento se represente, ref.current
podría ser null
, por lo que tiene el siguiente tipo:
HTMLDivElement | null
Para adjuntar un evento a ref.current
, primero tendríamos que manejar los null
valores posibles. Aquí hay un ejemplo:
import React from 'react'
const ToggleDisplay = () => {
const displayRef = React.createRef<HTMLDivElement>()
const toggleDisplay = () => {
if (displayRef.current) {
displayRef.current.toggleAttribute('hidden')
}
}
return (
<div>
<div class="display-panel" ref="displayRef">
<p> some content </p>
</div>
<button onClick={toggleDisplay}>Toggle Content</button>
<div>
)
}
En el fragmento anterior, tuvimos que manejar una verificación de tipo para displayRef.current
usar una if
declaración antes de llamar a la toggleAttribute
función.
En la mayoría de los casos, estamos seguros de que si onClick
se activa el evento del botón, nuestros elementos ya están representados. Por lo tanto, no hay necesidad de un control. Esta verificación innecesaria se puede eliminar usando el !
operador, así:
const displayRef = React.createRef<HTMLDivElement>()
const toggleDisplay = () => displayRef.current!.toggleAttribute('hidden')
return (
<div>
...
El !
operador no cambia el comportamiento de tiempo de ejecución de su código. Si el valor que ha afirmado no es null
o undefined
resulta ser null
o undefined
, se producirá un error e interrumpirá la ejecución de su código.
Recuerde, la diferencia entre TypeScript y JavaScript es la aserción de tipos. En JavaScript no necesitamos ni usamos el !
operador porque no hay tipo estricto.
Se puede crear una instancia de una variable de JavaScript string
y cambiarla a object
, null
o number
durante la ejecución del código. Esto deja en manos del desarrollador el manejo de los diferentes casos.
El uso del !
operador elimina el "superpoder" de TypeScript de evitar errores de tipo en tiempo de ejecución. Por lo tanto, no es la mejor práctica usar el !
operador.
Puede usar encadenamientos opcionales o predicados de tipos como alternativas a las aserciones no nulas.
El encadenamiento opcional es una forma abreviada de TypeScript que nos permite manejar fácilmente los casos en los que la variable está definida o no. Cuando la variable no está definida o es nula, el valor al que se hace referencia por defecto es value undefined
. He aquí un ejemplo de encadenamiento opcional:
interface Person {
name: string
age: number
sex: string
}
function printName(person?: Person): void {
console.log('The name of this person is', person?.name)
}
En nuestro ejemplo anterior, si person
no está definido, nuestra salida de impresión sería la siguiente:
'The name of this person is undefined'
El uso de predicados de tipo en TypeScript se realiza definiendo una función que realiza una prueba booleana y devuelve un predicado de tipo en el formulario arg is Type
. Aquí hay un ejemplo:
interface Person {
name: string
age: number
sex: string
}
function validatePerson(person?: Person) person is Person {
return !!person
}
Usando este tipo de predicado, primero podemos validar el objeto antes de realizar más operaciones, así:
function printName(person?: Person) {
if (!validatePerson(person)) {
console.log('Person is invalid')
return
}
console.log(`The name is ${person.name}`)
}
El poder de TypeScript sobre JavaScript es la seguridad de tipos que proporciona a nuestro código . Sin embargo, es posible que a veces queramos deshabilitar las comprobaciones estrictas de tipos de TypeScript, por ejemplo, en aras de la flexibilidad o la compatibilidad con versiones anteriores. En tales casos, podemos usar el operador de aserción no nulo.
Si bien es una característica útil, lo animo a explorar métodos de aserción de tipos más seguros. Puede ir un paso más allá para evitar el uso de esta operación en su proyecto y con su equipo agregando el typescript-eslint
paquete a su proyecto y aplicando la no-non-null-assertion
regla de lint .
Fuente: https://blog.logrocket.com/understanding-exclamation-mark-typescript/
1617257581
¿Quiere restaurar los buzones de correo de PST a Exchange Server? Entonces, estás en la página correcta. Aquí, lo guiaremos sobre cómo puede restaurar fácilmente mensajes y otros elementos de PST a MS Exchange Server.
Muchas veces, los usuarios necesitan restaurar los elementos de datos de PST en Exchange Server, pero debido a la falta de disponibilidad de una solución confiable, los usuarios no pueden obtener la solución. Háganos saber primero sobre el archivo PST y MS Exchange Server.
PST es un formato de archivo utilizado por MS Outlook, un cliente de correo electrónico de Windows y muy popular entre los usuarios domésticos y comerciales.
Por otro lado, Exchange Server es un poderoso servidor de correo electrónico donde todos los datos se almacenan en un archivo EDB. Los usuarios generalmente guardan la copia de seguridad de los buzones de correo de Exchange en el archivo PST, pero muchas veces, los usuarios deben restaurar los datos del archivo PST en Exchange. Para resolver este problema, estamos aquí con una solución profesional que discutiremos en la siguiente sección de esta publicación.
No le recomendamos que elija una solución al azar para restaurar los datos de PST en Exchange Server. Por lo tanto, al realizar varias investigaciones, estamos aquí con una solución inteligente y conveniente, es decir, Exchange Restore Software. Es demasiado fácil de manejar por todos los usuarios y restaurar cómodamente todos los datos del archivo PST a Exchange Server.
El software es demasiado simple de usar y se puede instalar fácilmente en todas las versiones de Windows. Con unos pocos clics, la herramienta puede restaurar los elementos del buzón de Exchange.
No es necesario que MS Outlook restaure los datos PST en Exchange. Todos los correos electrónicos, contactos, notas, calendarios, etc. se restauran desde el archivo PST a Exchange Server.
Todas las versiones de Outlook son compatibles con la herramienta, como Outlook 2019, 2016, 2013, 2010, 2007, etc. La herramienta proporciona varios filtros mediante los cuales se pueden restaurar los datos deseados desde un archivo PST a Exchange Server. El programa se puede instalar en todas las versiones de Windows como Windows 10, 8.1, 8, 7, XP, Vista, etc.
Descargue la versión de demostración del software de restauración de Exchange y analice el funcionamiento del software restaurando los primeros 50 elementos por carpeta.
No existe una solución manual para restaurar los buzones de correo de Exchange desde el archivo PST. Por lo tanto, hemos explicado una solución fácil e inteligente para restaurar datos de archivos PST en Exchange Server. Simplemente puede usar este software y restaurar todos los datos de PST a Exchange Server.
Más información:- https://www.datavare.com/software/exchange-restore.html
#intercambio de software de restauración #intercambio de restauración #buzón del servidor de intercambio #herramienta de restauración de intercambio
1647374400
Siempre que estamos creando software, a los desarrolladores generalmente les gusta dividir los problemas grandes en problemas más pequeños. Implementamos las soluciones a los subproblemas en componentes o módulos, luego integramos esos componentes para crear un sistema que resuelve el problema original.
Una de las cosas que determina la calidad de los sistemas y aplicaciones que construimos es el grado de interdependencia entre los módulos de software que componen el proyecto. Este grado de interdependencia se denomina dependencia o acoplamiento.
Por ejemplo, si tenemos un módulo (o clase) A que usa otro módulo (o clase) B, decimos que A depende de B, así:
class B {
}
class A {
private b = new B();
}
// class A depends on class B
A continuación se muestra un diagrama que representa la relación entre A y B:
Pero, antes de que podamos hablar sobre el Principio de Inversión de Dependencia, primero debemos analizar el concepto de acoplamiento débil frente a estrecho.
Uno de los signos de una aplicación bien estructurada (o sistema informático en general) es tener una interdependencia mínima (acoplamiento bajo) entre los componentes o módulos del sistema. No es deseable un sistema de componentes estrechamente acoplados.
Uno de los aspectos negativos de un sistema estrechamente acoplado es que el cambio en un módulo tendrá un efecto dominó de cambios en otros módulos que dependen del módulo modificado. Otro aspecto negativo es que un módulo puede volverse difícil de reutilizar y probar, porque se deben incluir sus módulos dependientes.
El Principio de Inversión de Dependencia es un principio que nos ayuda a acoplar módulos de software de forma flexible. Se llegó al principio después de muchos años de acoplar módulos de software por ingenieros de software profesionales, establece que:
Cuando abstraemos, podemos decir que estamos tratando solo con la idea general de algo sin preocuparnos por los detalles. Una forma importante de abstraer las cosas es mediante el uso de una interfaz.
El primer principio establece que tanto los módulos de alto nivel como los de bajo nivel deben depender de las mismas abstracciones. Si un módulo depende de una abstracción, por ejemplo, una interfaz o una clase abstracta, podemos cambiar la dependencia de la que depende por cualquier otra implementación que se adhiera a la interfaz.
Para una analogía de la vida real, tome, por ejemplo, el enchufe de un cargador de computadora portátil a continuación:
Este enchufe se puede conectar a cualquier enchufe siempre que el enchufe satisfaga la "interfaz" de los tres pines. De esta manera, el enchufe se puede usar con una variedad de enchufes, siempre que cumplan con los requisitos de la interfaz.
Supongamos que tenemos una Logger
clase para registrar información en nuestro proyecto, definida en un archivo logger.ts
de la siguiente manera:
export class Logger {
log(message: string) {
console.log(message);
}
error(message: string) {
console.error(message);
}
}
Estamos exportando la Logger
clase, ya que se espera que se use en otros lugares. Y lo estamos usando en otro módulo ubicado en un archivo llamado user-service.ts
:
import { Logger } from "./logger"; // this module depends on concrete implementatioon of logger
class UserService {
private logger = new Logger();
async getAll() {
try {
this.logger.log('Retrieving all users...');
return [];
} catch (error: any) {
this.logger.log(`An error occurred: ${error?.message}`);
throw new Error('Something went wrong')
}
}
}
Representemos la relación entre ambas clases de la siguiente manera:
La UserService
clase depende de la Logger
clase. Esto se hace evidente en primer lugar por el hecho de que tenemos que importar la Logger
clase a la UserService
clase.
Esta línea private logger = new Logger();
también revela que UserService
está estrechamente relacionado con la implementación actual de Logger
. Depende de una implementación concreta.
UserService
no se puede probar por separado sin el Logger
. Una forma de mejorar la capacidad de prueba de UserService
es cambiar la forma en que suministramos el Logger
objeto.
Actualmente, un Logger
objeto es instanciado UserService
por la línea private logger = new Logger();
. Podemos transferir la tarea de crear el objeto a otra parte de nuestro código y simplemente usamos el objeto directamente. Es decir, otra persona crea el objeto de dependencia y lo inyecta en UserService
. Esto se llama inyección de dependencia.
Un ejemplo de cómo hacer esto se muestra en el siguiente fragmento de código:
import { Logger } from "./logger";
class UserService {
private logger: Logger;
// the constructor receives the object to inject from another source
constructor(logger: Logger) {
this.logger = logger;
}
async getAll() {
try {
this.logger.log("Retrieving all users...");
return [];
} catch (error: any) {
this.logger.log(`An error occurred: ${error?.message}`);
throw new Error("Something went wrong");
}
}
}
La Logger
instancia ahora se inyecta en la UserService
clase a través del constructor, pero aún depende de una implementación concreta de Logger
, porque la logger
propiedad aún se escribe como Logger
.
Como sugiere el principio, queremos asegurarnos de que UserService
ambos Logger
dependan de abstracciones. No queremos importar nada de la clase de nivel inferior ( Logger
, en este caso) a la clase de nivel superior ( UserService
).
Introduzcamos un interface
y dejemos que UserService
dependa de él:
export interface ILogger {
log(message: string): void;
error(message: string): void;
}
class UserService {
private logger: ILogger;
constructor(logger: ILogger) {
this.logger = logger;
}
async getAll() {
try {
this.logger.log("Retrieving all users...");
return [];
} catch (error: any) {
this.logger.log(`An error occurred: ${error?.message}`);
throw new Error("Something went wrong");
}
}
}
Arriba, tenemos una ILogger
interfaz definida en UserService
. Este servicio define lo que se UserService
espera de un registrador; no está vinculado a una clase de registrador específica.
Cualquier clase que implemente la interfaz se puede inyectar en UserService
. Tenga en cuenta que la ILogger
interfaz no tiene que definirse en el user-service.ts
archivo, puede (y debe) extraerse en su propio archivo.
Ahora, actualizamos la Logger
clase para implementar la interfaz definida por UserService
:
import { ILogger } from "./user-service";
export class Logger implements ILogger {
log(message: string) {
console.log(message);
}
error(message: string) {
console.error(message);
}
}
Tenga en cuenta que UserService
no está importando nada Logger
en su implementación, y ambas clases dependen de una interfaz (una abstracción).
Entonces podemos hacer esto en cualquier lugar que queramos usar UserService
:
...
// inject any class that implements ILogger into UserService
const userService = new UserService(new Logger())
userService.getAll()
...
El diagrama de clases se verá así ahora:
Observe la dirección de las flechas. En el diagrama de clases anterior, había una flecha de UserService
a Logger
que indica que depende de Logger
. Ahora tenemos una flecha desde UserService
a la interfaz ILogger
. Además, tenemos una flecha de Logger
a ILogger
. Ambos dependen de una abstracción.
Hemos invertido con éxito la dirección de la dependencia. La inversión no significa que la capa de nivel inferior dependa directamente del nivel de nivel superior, significa que ambas capas deberían depender de abstracciones, y estas abstracciones exponen el comportamiento que necesitan las capas de nivel superior.
Ahora, para mostrar uno de los beneficios de aplicar el principio de inversión de dependencia, supongamos que nuestro proyecto es un proyecto de Node.js y queremos usar una biblioteca de registro de terceros para el registro. Winston es una de las bibliotecas de registradores populares que podemos elegir. Debido a que esta publicación no es una publicación sobre el registro, no entraré en detalles sobre cómo usar Winston.
Digamos que lo instaló winston
y configuró como se describe en los documentos , puede crear una clase que implemente ILogger
de la siguiente manera en un archivo llamado winston-logger.ts
:
import { ILogger } from "./user-service";
import * as winston from "winston";
export class WinstonLogger implements ILogger {
private winstonLogger: winston.Logger;
constructor() {
this.winstonLogger = winston.createLogger({
transports: [
new winston.transports.File({
format: winston.format.combine(
winston.format.json(),
winston.format.timestamp()
),
filename: "logs/combined.log",
}),
],
});
}
log(message: string): void {
this.winstonLogger.log(message);
}
error(message: string): void {
this.winstonLogger.error(message);
}
}
Configuramos el winston.transports.File
transporte para que los registros se puedan guardar en un archivo ubicado en la ruta del archivo logs/combined.log
, relativa a la raíz del proyecto. La WinstonLogger
clase también implementa la ILogger
interfaz.
UserService
puede hacer uso de WinstonLogger
, porque se adhiere a la interfaz que UserService
implementa de la siguiente manera:
const userService = new UserService(new WinstonLogger())
userService.getAll()
El diagrama de clases que representa la relación entre las clases se ve así:
Suponga que después de un tiempo decide que la winston
biblioteca de registradores no es la mejor para su proyecto y quiere usar Bunyan , ¿qué hace? Solo necesita crear una BunyanLogger
clase que implemente la ILogger
interfaz y esté lista para ser utilizada por UserService
.
La aplicación del principio de inversión de dependencia también garantiza que no estemos atados a una biblioteca de registro de terceros específica. No tenemos el control de la biblioteca, pero tenemos el control de cómo nuestro código de proyecto interactúa con él.
Conclusión
En este artículo, hemos explicado qué es una dependencia y por qué queremos un acoplamiento débil entre los componentes o módulos que componen nuestra aplicación TypeScript. También vimos el Principio de Inversión de Dependencia, un ejemplo práctico de cómo aplicarlo y cómo nos permite intercambiar implementaciones fácilmente.
El principio de inversión de dependencia es uno de los principios de los populares principios SOLID, que es un acrónimo de los primeros cinco principios de diseño orientado a objetos de Robert C. Martin.
Enlace: https://blog.logrocket.com/understanding-dependency-inversion-principle-typescript/
1617255938
Si tiene problemas para migrar los buzones de correo de Exchange a Office 365, debe leer este artículo para saber cómo migrar los buzones de correo de Exchange EDB a Office 365. Al migrar a Office 365, los usuarios pueden acceder a sus buzones de correo desde cualquier lugar y desde cualquier dispositivo.
En esta publicación, explicaremos las razones detrás de esta migración y una solución profesional para migrar de Exchange a Office 365.
Office 365 apareció por primera vez en 2011 y, dado que se considera la mejor plataforma para aquellas organizaciones que desean administrar todo su sistema de correo electrónico en la nube. Estas son las características clave de Office 365:
Hay varias formas manuales de migrar los buzones de correo de Exchange EDB a Office 365, pero para evitar estos complicados y prolongados procedimientos, presentamos una solución de terceros, es decir, la herramienta de migración de Exchange, que es automatizada y directa para la migración de Exchange a Office 365. La herramienta funciona rápidamente y migra todos los elementos del buzón de Exchange Server a Office 365.
La herramienta de migración de Datavare Exchange es demasiado fácil de usar y ofrece pasos sencillos para migrar EDB a Office 365:
Por lo tanto, todos sus buzones de correo de Exchange EDB ahora se migran a Office 365.
Nota: puede usar filtros para migrar los elementos de datos deseados de la cuenta de Exchange a la de Office 365
Este blog le indica una solución profesional para la migración de buzones de correo de Exchange a la cuenta de Office 365. Dado que las soluciones manuales son complicadas, sugerimos la herramienta de migración de Exchange, que es demasiado simple de usar. Los usuarios no se enfrentan a problemas al operar el programa. La mejor parte de este software es que no necesita habilidades técnicas para realizar la migración. Se puede comprender el funcionamiento del software descargando la versión de demostración que permite la migración de los primeros 50 elementos por carpeta.
Más información:- https://www.datavare.com/software/edb-migration.html
#herramienta de migración de intercambio #migración de intercambio #migrar buzones de correo de exchange