Bienvenidos sean a este post, sobre un tema muy parecido a lo visto en otros lenguajes pero no igual.
En este post mencionamos un mecanismo denominado «Tipo Duck» y se usa principalmente para comparar dos objetos y determinar si son iguales tanto en el tipo como en las propiedades y metodos internos, en caso de poseerlos.
Las interfaces nos proveen un mecanismo para establecer que propiedades podemos implementar en un objeto y por lo tanto nos permite definir tipos personalizados. Al momento de definir una interface, lo que estamos haciendo es declarar las propiedades y metodos que tendra el objeto que sera usado por nuestro codigo. Consideremos el siguiente codigo:
interface IObjeto {
id: number;
nombre: string;
}
let idObjeto: IObjeto = {
id: 2
}
Lo primero que definimos es la interfaz donde le asignaremos un nombre identificador. Dentro de este declaramos dos propiedades con dos tipos de datos, en este caso number y string. Nuestro siguiente paso sera definir un objeto donde el tipo sera la interfaz anterior y en este objeto definiremos una sola de las propiedades anteriores. Compilemos y veamos su salida:
$ tsc object.ts
object.ts:5:5 - error TS2741: Property 'nombre' is missing in type '{ id: number; }' but required in type 'IObjeto'.
5 let idObjeto: IObjeto = {
~~~~~~~~
object.ts:3:2
3 nombre: string;
~~~~~~
'nombre' is declared here.
Found 1 error in object.ts:5
$
Como no definimos la segunda propiedad, este nos lo notifica y nos detuvo la compilacion. Para solucionarlo simplemente debemos definirlo al momento de crear el objeto y con eso ya bastaria pero tenemos una opcion interesante para corregirlo.
Efectivamente, en interfaces tambien disponemos del operador opcional para poder declarar a las propiedades como opcionales. Para entender el concepto tomemos el codigo anterior y hagamos la siguiente modificacion:
interface IObjeto {
id: number;
nombre?: string;
}
let idObjeto: IObjeto = {
id: 2
}
La unica modificacion que hicimos fue agregar el operador para la segunda propiedad donde a partir de ahora este quedara como opcional. Si lo compilamos nuevamente y lo ejecutan este funcionara perfectamente porque ya no se reportara el error.
Con esto podemos comentar que las interfaces no generan ningun codigo de javascript y solo son un constructo usado en los pasos de compilacion de typescript y servicios del lenguaje. Por lo tanto, solo estan para mantener el type-safe al momento de trabajar con typescript porque si tomamos el codigo anterior y vemos el codigo de javascript sera de la siguiente manera:
var idObjeto = {
id: 2
};
Solamente nos generara el objeto final creado a partir de la interfaz sin la definicion de la misma por lo cual podemos decir que las interfaces se compilan a parte.
La nomenclatura que utilizamos para identificar a la interfaz es comenzando con la I. Esto es una buena practica y/o recomendacion pero no es estrictamente obligatorio y se utiliza de esta forma para que sepamos al ver el nombre que es una interfaz.
Antes comentamos que mediante el operador opcional podiamos establecer cual propiedad puede ser opcional para el compilador permitiendo omitir dicha propiedad pero que sucede cuando establecemos todas las propiedades como opcionales? Estas se las consideran como tipos weak, para entender el concepto tomemos el codigo anterior y realicemos la siguiente modificacion:
interface IObjeto {
id?: number;
nombre?: string;
}
let idObjeto: IObjeto = {
desc: "una descripcion"
}
Aqui establecimos que las dos propiedades de nuestra interfaz pasaron a ser opcionales. La siguiente modificacion es en el objeto donde estableceremos otra propiedad en lugar de las dos opcionales. Veamos que sucede:
$ tsc object.ts
object.ts:6:2 - error TS2353: Object literal may only specify known properties, and 'desc' does not exist in type 'IObjeto'.
6 desc: "una descripcion"
~~~~
Found 1 error in object.ts:6
$
Oopss, nos devolvio un error pero por que? La respuesta es muy simple, a pesar de tener tipos weak y saber que las propiedades son opcionales el compilador igualmente sigue utilizando un type-safe y al no encontrar esta propiedad nos notifica esto. Por lo tanto por mas que las seteamos como opcionales solamente podremos usar las que declaramos, tengan esto en cuenta al momento de usarlo.
Un operador muy interesante que disponemos es in porque nos permite interrogar al objeto si posee una propiedad. Para entender el concepto vamos a analizar con el siguiente codigo:
interface INombreId {
id: number;
nombre: string;
}
interface IDescVal {
desc: string;
valor: number;
}
function nombreOvalor(obj: INombreId | IDescVal): void
{
if ('id' in obj) {
console.log("obj.nombre: " + obj.nombre);
}
if ('desc' in obj) {
console.log("obj.valor: " + obj.valor);
}
}
nombreOvalor({ id: 1, nombre: "objeto" });
nombreOvalor({ desc: "Es un objeto", valor: 256 });
Primero definimos dos interfaces, en la primera tenemos dos propiedades para el id y el nombre del objeto. En la segunda tenemos la descripcion y el valor del objeto, con sus respectivos tipos. Lo siguiente es una funcion donde usaremos la union de tipos para pasar las interfaces anteriores para el argumento de la funcion. El tipo a ser void no importa el que debemos devolver como sucede en otros lenguajes. Dentro del bloque tenemos dos condicionales muy similares, en el primer caso mediante in buscaremos en el objeto informado si existe la propiedad. En caso de encontrarla procede a mostrar el valor de nombre, la otra propiedad de la misma interfaz. En el segundo condicional hacemos exactamente lo mismo pero la propiedad desc y en caso de existir mostrara el otro valor, en este caso la propiedad valor. Ya tenemos la funcion para mostrar nombres o valores y por ultimo tenemos dos llamadas a la funcion anterior con los valores de cada interfaz. Con todo comentado compilemos y veamos como es su salida:
$ node object.js
obj.nombre: objeto
obj.desc: 256
$
Como pueden ver funciono correctamente, pero a todo esto tambien podemos aplicarle un guardian de tipos para hacerlo mas seguro.
Typescript posee una herramienta para iteratuar por todos los nombres de las propiedades de una interfaz, esta se realiza mediante la palabra keyof. El resultado obtenido trabaja de la misma forma que los tipos literal, del cual hablamos en este post, para entender el concepto vamos a analizar el siguiente codigo:
interface IPersona {
id: number,
nombre: string
}
type persona = keyof IPersona;
function propiedad(clave: persona, valor: IPersona)
{
console.log(clave + ": " + valor[clave]);
}
propiedad("id", {id: 1, nombre: "Martin"});
propiedad("nombre", { id: 2, nombre: "Enzo"});
Primero definimos la interfaz con dos propiedades de distintos tipos. Lo siguiente es definir un alias donde almacenaremos el resultado devuelto por keyof aplicado a la interfaz, el resultado obtenido equivale a lo siguiente:
type persona = "id" | "nombre";
Este es basicamente un literal de lo cual hablamos en este post, con nuestro literal establecido pasemos a hablar sobre la funcion. En esta aplicaremos primero los literales para el argumento clave, ya que solo tenemos esos valores, y el siguiente argumento tiene como tipo a la interfaz para poder tener las claves y valores, en el bloque mostraremos el resultado de la clave enviada y el valor de la clave informada. Luego haremos dos llamados a esta funcion con distintos datos, compilemos y veamos como es su salida:
$ node object.js
id: 1
nombre: Enzo
$
Obtuvimos los datos que solicitamos en cada caso, tomemos el codigo anterior y al final agreguemos la siguiente linea:
propiedad("telefono", { id: 3, nombre: "tinchicus"});
Volvemos a llamar a la funcion pero ahora pasaremos otra clave, veamos como es su sallida:
tinchicus@dbn001vrt:~/lenguajes/ts/20$ tsc object.ts
object.ts:14:11 - error TS2345: Argument of type '"telefono"' is not assignable to parameter of type 'keyof IPersona'.
14 propiedad("telefono", { id: 3, nombre: "tinchicus"});
~~~~~~~~~~
Found 1 error in object.ts:14
tinchicus@dbn001vrt:~/lenguajes/ts/20$
Tal como comentamos al utilizar a un literal para verificar cuando pasemos una clave que no este dentro del mismo nos devolvera un error para informarnos esto. Como dijimos esta es una forma muy practica para obtener las claves y poder usarlas asi como un tipo literal.
Hoy hemos visto interfaces, que son, para que sirve, como se utilizan, las distintas opciones que podemos aplicar, asi como tambien las ventajas que podemos obtener de su uso, espero les haya resultado de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donación
Es para mantenimento del sitio, gracias!
$1.50
