La interfaz es una estructura que define el contrato en su aplicación. Define la sintaxis que deben seguir las clases. Las clases que derivan de una interfaz deben seguir la estructura proporcionada por su interfaz.
El compilador de TypeScript no convierte la interfaz en JavaScript. Utiliza la interfaz para la comprobación de tipos. Esto también se conoce como «duck typing» o «structural subtyping».
Una interfaz se define con la palabra clave interface
y puede incluir propiedades y declaraciones de métodos utilizando una función o una función de flecha.
Copia
interface IEmployee { empCode: number; empName: string; getSalary: (number) => number; // arrow function getManagerName(number): string; }
En el ejemplo anterior, la interfaz IEmployee
incluye dos propiedades empCode
y empName
. También incluye una declaración de método getSalaray
que utiliza una función de flecha que incluye un parámetro numérico y un tipo de retorno numérico. El método getManagerName
se declara utilizando una función normal. Esto significa que cualquier objeto de tipo IEmployee
debe definir las dos propiedades y los dos métodos.
Interfaz como tipo
La interfaz en TypeScript puede utilizarse para definir un tipo y también para implementarlo en la clase.
La siguiente interfaz IEmployee
define un tipo de una variable.
Copiar
interface KeyPair { key: number; value: string;}let kv1: KeyPair = { key:1, value:"Steve" }; // OKlet kv2: KeyPair = { key:1, val:"Steve" }; // Compiler Error: 'val' doesn't exist in type 'KeyPair'let kv3: KeyPair = { key:1, value:100 }; // Compiler Error:
En el ejemplo anterior, una interfaz KeyPair
incluye dos propiedades key
y value
. Una variable kv1
se declara como tipo KeyPair
. Por lo tanto, debe seguir la misma estructura que KeyPair
. Significa que sólo un objeto con propiedades key
de tipo número y value
de tipo cadena puede ser asignado a una variable kv1
. El compilador de TypeScript mostrará un error si hay algún cambio en el nombre de las propiedades o el tipo de datos es diferente a KeyPair
. Otra variable kv2
también está declarada como de tipo KeyPair
pero el valor asignado es val
en lugar de value
, por lo que provocará un error. Del mismo modo, kv3 asigna un número a la propiedad value
, por lo que el compilador mostrará un error. Así, TypeScript utiliza una interfaz para asegurar la estructura adecuada de un objeto.
Interfaz como tipo de función
La interfaz de TypeScript también se utiliza para definir el tipo de una función. Esto asegura la firma de la función.
Copia
interface KeyValueProcessor{ (key: number, value: string): void;};function addKeyValue(key:number, value:string):void { console.log('addKeyValue: key = ' + key + ', value = ' + value)}function updateKeyValue(key: number, value:string):void { console.log('updateKeyValue: key = '+ key + ', value = ' + value)} let kvp: KeyValueProcessor = addKeyValue;kvp(1, 'Bill'); //Output: addKeyValue: key = 1, value = Bill kvp = updateKeyValue;kvp(2, 'Steve'); //Output: updateKeyValue: key = 2, value = Steve
En el ejemplo anterior, una interfaz KeyValueProcessor
incluye una firma de método. Ésta define el tipo de función. Ahora, podemos definir una variable de tipo KeyValueProcessor
que sólo puede apuntar a funciones con la misma firma definida en la interfaz KeyValueProcessor
. Así, addKeyValue
o updateKeyValue
función se asigna a kvp
. Así, kvp
puede ser llamado como una función.
Intentar asignar una función con una firma diferente causará un error.
function delete(key:number):void { console.log('Key deleted.')} let kvp: KeyValueProcessor = delete; //Compiler Error
Interfaz para Tipo de Array
Una interfaz también puede definir el tipo de un array donde se puede definir el tipo de índice así como los valores.
Copiar
interface NumList { :number}let numArr: NumList = ;numArr;numArr;interface IStringList { :string}let strArr : IStringList;strArr = "TypeScript";strArr = "JavaScript";
En el ejemplo anterior, la interfaz NumList
define un tipo de array con índice como número y valor como tipo número. Del mismo modo, IStringList
define una matriz de tipo cadena con índice como cadena y valor como cadena.
Propiedades opcionales
A veces, podemos declarar una interfaz con exceso de propiedades pero no esperar que todos los objetos definan todas las propiedades de la interfaz dadas. Podemos tener propiedades opcionales, marcadas con un «?». En estos casos, los objetos de la interfaz pueden o no definir estas propiedades.
Copiar
interface IEmployee { empCode: number; empName: string; empDept?:string;}let empObj1:IEmployee = { // OK empCode:1, empName:"Steve"}let empObj2:IEmployee = { // OK empCode:1, empName:"Bill", empDept:"IT"}
En el ejemplo anterior, empDept
está marcado con ?
, por lo que los objetos de IEmployee
pueden o no incluir esta propiedad.
Propiedades de sólo lectura
TypeScript proporciona una forma de marcar una propiedad como de sólo lectura. Esto significa que una vez que se asigna un valor a una propiedad, no se puede cambiar.
Copia
interface Citizen { name: string; readonly SSN: number;}let personObj: Citizen = { SSN: 110555444, name: 'James Bond' }personObj.name = 'Steve Smith'; // OKpersonObj.SSN = '333666888'; // Compiler Error
En el ejemplo anterior, la propiedad SSN
es de sólo lectura. Definimos el objeto personObj de tipo Ciudadano y asignamos valores a las dos propiedades de la interfaz. A continuación, intentamos cambiar los valores asignados a ambas propiedades-name
y SSN
. El compilador de TypeScript mostrará un error cuando intentemos cambiar la propiedad SSN
de sólo lectura.
Extender interfaces
Las interfaces pueden extender una o más interfaces. Esto hace que la escritura de interfaces sea flexible y reutilizable.
Copiar
interface IPerson { name: string; gender: string;}interface IEmployee extends IPerson { empCode: number;}let empObj:IEmployee = { empCode:1, name:"Bill", gender:"Male"}
En el ejemplo anterior, la interfaz IEmployee
extiende la interfaz IPerson
. Por lo tanto, los objetos de IEmployee
deben incluir todas las propiedades y métodos de la interfaz IPerson
de lo contrario, el compilador mostrará un error.
Implementación de una interfaz
De forma similar a lenguajes como Java y C#, las interfaces en TypeScript se pueden implementar con una Clase. La Clase que implementa la interfaz necesita ajustarse estrictamente a la estructura de la interfaz.
Copiar
interface IEmployee { empCode: number; name: string; getSalary:(number)=>number;}class Employee implements IEmployee { empCode: number; name: string; constructor(code: number, name: string) { this.empCode = code; this.name = name; } getSalary(empCode:number):number { return 20000; }}let emp = new Employee(1, "Steve");
En el ejemplo anterior, la interfaz IEmployee
se implementa en la clase Employee utilizando la palabra clave implement. La clase implementadora debe definir estrictamente las propiedades y la función con el mismo nombre y tipo de datos. Si la clase implementadora no sigue la estructura, entonces el compilador mostrará un error.
Por supuesto, la clase implementadora puede definir propiedades y métodos extra, pero al menos debe definir todos los miembros de una interfaz.
En el próximo capítulo, aprenderemos más sobre las clases de TypeScript.