Conceptos basicos de Typescript

Christian Russo
orbit-software
Published in
13 min readSep 21, 2020

--

Typecript es un superconjunto de Javascript. Esto significa que cualquier javascript que escriba sigue siendo un mecanografiado válido. ¿En qué se diferencia el texto mecanografiado entonces? TypeScript extiende javascript agregando tipos, características que aún no están en javascript y algunas otras utilidades. El final del día mecanografiado se compila en javascript. En simple, incluso si escribe mecanografiado, javascript es lo que se envía al navegador.

¿Por qué incluso necesitamos mecanografiado, javascript no es suficiente?

Déjame explicarte con un ejemplo:

Escribiste una función de suma:

const sum = (a, b) => a + b;

Siempre que llame a la función de suma con dos argumentos numéricos, funcionará bien. Pero, ¿qué pasa si pasa una cadena y un número o ambas cadenas o incluso un número y un objeto? Esto no es un problema si su proyecto tiene solo un par de líneas de códigos y solo una o dos personas están trabajando en él. Pero, ¿qué sucede si su producto tiene unos pocos miles de líneas de código o incluso algunos cientos? Agregue algunas personas más que trabajen en el proyecto y utilicen la función de suma. Es posible que todos ellos no conozcan los aspectos internos de la función. Ahora uno de ellos puede llamar a la función con tipos de argumentos incorrectos. Esto dará como resultado resultados inesperados. Puede conducir a una experiencia terrible si esto se lleva a producción.

El problema en el escenario anterior se puede detectar antes con mecanografiado. TypeScript se quejará de usted si está intentando pasar una cadena a una declaración de tipos de números.

Veamos la declaración mecanografiada equivalente de la función suma en mecanografiado

const sum = (a: number, b: number) => a + b;
// Now if you try to pass a non-number value to a or b typescript will complainconst s1 = sum(3, 5);
// works
const s2 = sum(2, '33');
// Error
// Argument of type '"33"' is not assignable to parameter of type 'number'

TypeScript requiere alguna instalación y configuración para funcionar en un proyecto real, pero para seguir este artículo puede utilizar cualquiera de los siguientes enlaces

Tipos 101: los conceptos básicos de los tipos en mecanografiado

En un término simple, puede pensar en los tipos de mecanografiado como el tipo de la variable. Puede ser definido explícitamente (por usted) o puede inferirse implícitamente de la asignación (por el compilador).

Sintaxis (tipos explícitos):

// declaration:type = assignmet; (assignment is optional for let, var)
// const someConst: string = 'some const';
// let someNumber:number = 44;
// examples:// here the type of num varialbe is number, you cannot assign other types to the variable num.let num: number = 44;// valid
num = 8595;
// invalid
num = '333';
// validnum = 3445.88;// One more examplelet str: string = 'hello, I can only be a string';// valid
str = 'Just another string';
// invalid
str = 22;
// one more exaple with only declaration but no assignmetlet someNumber:number;// valid
someNumber = 55;
// invalid
someNumber = 'I am not a number';
// invalid
someNumber = '333';
// valid
someNumber = Infinity;

Sintaxis (tipos implícitos):

// following line declares a variable implicitNum, type of number and assigns 44 to it. The value assigned to it is a number, so typescript automatically determines the type of the variable. This is the type inference.let implicitNum = 44;
// valid
implicitNum = 66;
// invalid
implicitNum = 'Not allowed';

Como estamos familiarizados con los tipos, ahora aprendamos los tipos más básicos en mecanografiado:

  • Boolean
  • Number
  • String
  • Array
// Booleanconst alwaysTrue: boolean = true;
let onlyBool: boolean = true;
// valid
onlyBool = false;
// invalid
onlyBool = 'true';
// Following operation is invalid because + operator is only defined for string and numbers
const addition = alwaysTrue + onlyBool;
// Numberconst againSomeNum = 55;
let someNumberAgain = 66;
// invalid
someNumberAgain = '44';
// valid
someNumberAgain = 365;
// Stringlet someStrAgain = 'Yes, this is a string';// validsomeStrAgain = 'ok, valid';// invalidsomeStrAgain = 4345;
someStrAgain = true;
// Array// There are two ways to declare an array// normal array syntax --const someArray:number[] = [1, 2, 3, 4, 5];// valid
someArray.push(34546);
// invalid
someArray.push('ok');
someArray.push(true);
// Generic array syntax --const oneMoreArray: Array<number> = [34, 35, 23, 21];// valid
oneMoreArray.push(4354);
oneMoreArray.push(34.234);
// invalid
oneMoreArray.push('not valid!');

Algunos tipos más básicos en mecanografiado

Any

Es la forma más sencilla de empezar a escribir a máquina. Es un supertipo para todo tipo de letra. Cualquiera es una forma de excluirse de la verificación de tipos. Entonces, puede asignar un valor de cualquier tipo a este tipo de variable y viceversa.

Este puede ser el único amigo suyo cuando está tratando de comenzar con el mecanografiado. Pero no te quedes con el chico, ya que te va a malcriar. Otros tipos son más leales y, por tanto, dignos de confianza.

// Anylet assignAnything:any = 44;assignAnything = 'Assign string';assignAnything = true;assignAnything = false;assignAnything = [35, 45, 44];let arrayOfAnyType: any[] = [454, 435, 'string also', false];// You can even access properties on an non object variable// Even the code below fails, typescript doesn't complainassignAnything.sayHello();// Assin to any type of variable Quite risky, right?let strictlyNumber:number = 66;
const anAnyTypeOfVariable: any = 'Not a number, but can be assigned to number';
// valid
strictlyNumber = anAnyTypeOfVariable;

Unknown

El tipo desconocido es similar a cualquiera, puede asignar cualquier tipo de valor a un tipo desconocido de variable. Pero la diferencia entre cualquiera y desconocido es que no puede asignar un tipo de valor desconocido a otros tipos de variables excepto para escribir Any.

// Unknownlet unknownVariable:unknown = 44;assignAnything = 'Assign string';assignAnything = true;assignAnything = false;assignAnything = [35, 45, 44];let unknownArray: unknown[] = [454, 435, 'string also', false];// You cannot access properties on an unknown type of vlaue// invalidunknownVariable.sayHello();// Cannot assign to other typeslet someStrictNumber:number = 66;const unknownValueAgain: unknown = 'Not a number, but can be assigned to number';// invalidsomeStrictNumber = unknownValueAgain;// but can assign to any typelet someAnyTypeVariable: any = [34, 5];let someUnknownVariable:unknown = 'unknown value';// validsomeAnyTypeVariable = someUnknownVariable;

Null and Undefined

El comportamiento de los tipos Null y Undefined depende de una bandera (- strictNullCheck) pasada al compilador. Se recomienda establecer este indicador en verdadero. Cuando la bandera no está establecida, Null e Undefined son subtipos de todos los demás tipos. Puede asignarlos a cualquier variable de todos los demás tipos. Pero cuando el indicador (- strictNullCheck) se establece en Null y Undefined solo se pueden asignar a su tipo respectivo, cualquiera y tipos desconocidos. Y undefined también se puede asignar al tipo de vacío.

// Nulllet nullValue:null = null;// invalidnullValue = 33;let someNumberA: number = 333;someNumberA = nullValue;// validlet anyValueA: any = 354;anyValueA = nullValue;let unknowValueA: unknown = 3543;unknowValueA = nullValue;// Undefinedlet undefinedValueA: undefined = undefined;// invalidundefinedValueA = 33;let someNumberB: number = 333;someNumberB = undefinedValueA;// validlet anyValueB: any = 354;anyValueA = undefinedValueA;let unknowValueB: unknown = 3543;unknowValueA = undefinedValueA;

Void

Type Void se utiliza para declarar el tipo de retorno de funciones que no devuelve nada o nulo pero finaliza la ejecución.

// The example will be more clear once we learn type function. Here, the function greets return type is void. In simple its return value doesn't matter.function greet(name: string): void {
console.log(`hello ${name}`);
// even this function is returning nothing explicitly, it is returning undefined implicityly. We already know undefined is assignable to void. So, this is valid
};
// void keyword in javascript discards the value returned. so the following declaration is totaly validfunction discardTheReturnValue(name: string): void {console.log(`hello ${name}`);return void 333;};

Never

Never type se usa para declarar el tipo de retorno de funciones que nunca regresan (se ejecuta infinitamente o arroja un error). Nunca se puede asignar a ningún tipo, pero no se puede asignar ningún tipo a nunca (incluso ninguno se puede asignar a nunca).

// Never// Both functions below never returnsfunction throwSomeError():never {throw new Error('I am only throwing error');}function neverReturn():never {while(true) {}}

Enum

Las enumeraciones son una forma de dar nombres y organizar valores numéricos. Puede definir una enumeración con posibles valores numéricos (con sus nombres) y acceder a ellos con notación de puntos similar a un objeto.

// Enumenum Fruit {Apple,Orange,Melon,}let f: Fruit = Fruit.Apple;console.log(f === 0) // trueconsole.log(f === Fruit.Apple) // trueconsole.log(f === Fruit.Orange) // false

Tipos más complejos y tipificación en mecanografiado

A estas alturas, debería poder crear tipos para declaraciones primitivas. Es hora de profundizar en más tipos.

Object

Hay muchas formas de declarar el tipo de un objeto.

// Object// 1. object type (Not recommended)// can be used to type anything except number, string, boolean, symbol, null, or undefined.let someObj:object;someObj = {
someNo: 123,
someString: 'hi there!',
sayHi: () => { console.log('Hi') }
};
// valid (these can lead to bugs, that is exactly why this approach is not recommended)someObj = [];
someObj = () => {
console.log('another function');
}
// invalidsomeObj = 2354;// 2. Adhoc object declaration: An actual object like structure describing properties and methodslet anotherObject: { someNo: number; someString: string; sayHi: () => void } = {
someNo: 345,
someString: "hi",
sayHi: () => {
console.log("hi");
}
};
// invalidanotherObject = [];
anotherObject = () => {
console.log('this is also invalid');
}
anotherObject.somethingNotInType = 'Invalid, because the property is not in type';
anotherObject.someNo = 'Invalid, someNo is of type number';

Function

También hay muchas formas de declarar (escribir) una función.

// Function// 1. Function type (doesn't care about args and return types)let someTypedFunction:Function = () => {
console.log('Any function could have been assigned to this variable');
}
someTypedFunction = (a: number, b: number) => {
const sum: number = a + b;
return sum;
}
// valid, because the type doesn't care about the args and return types.someTypedFunction();// invalidsomeTypedFunction = 3545;// 2. Explicit (Adhoc) function type declaration// Here, we declare the arguments inside the function declaration parenthesis
// Name of the arguments in the declaration and in the types does not have to match
// Return type is declared after the fat arrow (=>)
// eg: function below takes two argument, both of type number
// Returns a number
// You can only assign a function which matches this signature
let someAnotherTypeFunction: (arg1: number, arg2: number) => number = (a, b) => {
return a + b;
};
// validsomeAnotherTypeFunction(44, 22);// invalidsomeAnotherTypeFunction(34, '345');
const returnedValue:string = someAnotherTypeFunction(345, 346);
someAnotherTypeFunction = () => {
console.log('Invalid, this does not match the type declaration');
}
// valid// You can always use less parameters then decalaredsomeAnotherTypeFunction = () => {
return 3545;
}
// Invalid// You cannot use more parameters than declared in the typesomeAnotherTypeFunction = (a, b, c) => {
return a + b + c;
}
// validsomeAnotherTypeFunction = (nameDoesNotMatter1, nameDoesNotMatter2) => {
return nameDoesNotMatter1 * nameDoesNotMatter1;
}

Class

Class es una característica nueva en javascript. Para comprender bien el concepto de Clases, es necesario comprender la programación orientada a objetos. Por ahora, veamos una declaración de clase simple.

// Classclass Person {// property type declarationname: string;
age: number = 0;
// just like a simple function declaration right?
// But it implicitly returns the instance of the class -- type of class
// the Person in this case
constructor(name: string, age: number) {
// valid
this.name = name;
this.age = age;
// invalid, the property type is not declaredthis.wow = 'wow!';
}
// just like a normal function againsetAge: (age: number) => void = (age) => {
this.age = age;
}
sayHi() {
console.log(`Hi, ${this.name}`);
}
}
// user2 is a type of Personlet user2: Person = new Person('User 2', 34);// user1 is also of type Person, implicitly set by the compilerlet user1 = new Person('User 1', 35);// invalid
user1 = 'invalid, users type is Person';

La clase es un concepto bastante amplio en mecanografiado. Le recomendaría que consulte la documentación de las clases en la documentación oficial mecanografiada.

Tipo de aserciones

Hay ocasiones en las que se encontrará con escenarios, como que alguna declaración de typeA deba usarse con alguna declaración diferente de typeB.

// Type assertions (also referd as typecasting sometimes) ---------const someUnknownType: unknown = 'I am of type unknown';// inside some block you know that the type of the variable is string
// Now even if you know the type complier is still unaware of this
// so accessing string methods on the variable is still invalid
someUnknownType.toLowerCase();// Don't worry, You can still assert the type or tell the compiler that I know what I am doing
// type of the varialbe is actually a string
// and then you are allowed to access string methods
(someUnknownType as string).toLowerCase();// Here is the syntax for typecasting// 1. as keyword --> someTypeA as someTypeB// exampleconst unknownTypeA: unknown = 354;const someNumberC: number = unknownTypeA as number;// 2. angular brackets --> <someTypeB>someTypeA// the syntax above changes the type of someTypeA to someTypeBconst unknowTypeB: unknown = [35, 345];const someArrayType: number[] = <number[]>unknowTypeB;

Recuerde que todo este casting solo ocurre en el tipo y no en el valor real. Seamos claros con un ejemplo

let someNumberType: number = 34546;const someUnknownValue: unknown = 'This is unknown';// vlaue of someUnknownValue is not converted to number, only the type has changed here.someNumberType = someUnknownValue as number;console.log('typeof stil returns string -', typeof someNumberType);

Esto puede provocar errores. Por eso no se recomiendan las afirmaciones.

Reutilizar y componer tipos con Type, Interface, union e intersección

Ya sabes lo suficiente mecanografiado para empezar. Pero en el código de la aplicación real, tendrá muchas declaraciones con los mismos tipos. Las declaraciones de tipos primitivos son fáciles con solo una palabra, y hay muchas menos posibilidades de que dos tipos no coincidan. Pero, ¿qué pasa con los otros tipos, como las funciones o los objetos? ¿Qué pasa si tienes cientos de objetos del mismo tipo? ¿Qué sucede si tiene muchas funciones que reciben los mismos tipos de funciones de devolución de llamada? ¿Declara tipos ad-hoc en todas partes? ¿Qué pasa si sus declaraciones se estropean de alguna manera?

¡No te preocupes! Typecript ya ha pensado en esto. Nos proporciona las declaraciones de tipo e interfaz que se pueden reutilizar.

Tipo

La palabra clave Type le permite crear sus propios tipos componiendo otros tipos.

// Type// simplest typestype JustNumber = number;// Now JustNumber can be used as a type
// anywhere we refer to JustNumber it will refer to a type assigned to it
// number in this example
let someNumberConst: JustNumber = 98798;someNumberConst = 3498;// invalid
someNumberConst = 'Invalid, string cannot be assigned to type of JustNumber';
// type doesn't has to be this simple. Remember the object declarations
// Now the type declaration can be re-used
type SomePerson = {
name: string,
age: number,
favColors: string[],
sayHi: () => void,
}
const person1: SomePerson = {
name: 'Person 1',
age: 45,
favColors: ['blue', 'pink', 'purple', 'red', 'black', 'white'],
sayHi: () => console.log('hi'),
};
const person2: SomePerson = {
name: 'Person 1',
age: 78,
favColors: ['blue', 'pink', 'purple', 'red', 'black', 'white'],
sayHi: () => console.log('hi'),
};
// It is not restricted to only objects. Look at the function declaration belowtype BinaryOperatorOnNumber = (a: number, b: number) => number;const add: BinaryOperatorOnNumber = (a, b) => a * b;// argument name does not needs to be exact only the type and sequence mattersconst multiply: BinaryOperatorOnNumber = (num1, num2) => num1 * num2;

Interfaz

Al igual que el Type, las interfaces también nos permiten crear tipos reutilizables. Las interfaces se utilizan para definir la estructura (tipo) de variables o valores.

// Interface// basic syntax// interface interfaceName {
// ... properties
// }
interface DifferentPerson {
name: string,
age: number,
}
const p3: DifferentPerson = {
name: 'p3',
age: 23,
};
// interfaces can be extended
// the new interface will have new properties defined as well as
// properties from the old declarations
interface Teacher extends DifferentPerson {
teaches: string,
}
// Now Teacher has all of the DifferentPersons fields as well as new fields definedconst t1: Teacher = {
name: 'Some teacher',
age: 325,
teaches: 'typescript',
};
// interface can also be used to define function types
// The earlier sum function declaration can be done with intefaces
interface Sum {(a: number, b: number): number;}const addNumbers: Sum = (a, b) => a + b;

Y hay algunas formas más de reutilizar el código

Unión

¿Qué sucede si necesita crear un tipo que sea uno de los muchos ya declarados? Ahí es donde los tipos de Union vienen a ayudarlo.

// Union// syntax --// typeA | typeB | typeC
// As you can see symbol '|' is used to do an union.
// a string or a number can be assigned to the variable inputlet input: string | number = 44;
input = 55;
input = 'this also valid';
// invalidinput = true;// the types don't need to be so simpletype A = {
a: string,
b: string,
};
type B = {
c: string,
d: string,
};
// either a value of type A or B can be assigned to the below declarationtype AOrB = A | B;const onlyA: A = {
a: 'a',
b: 'b',
};
const onlyB: B = {
c: 'c',
d: 'd',
};
let aOrB: AOrB = onlyB;
aOrB = onlyA;
// Not valid
aOrB = {
notValid: 'Yes this is not valid',
};

Intersección

Intersection combina varios tipos en un solo tipo.

// Intersection// syntax
// typeA & typeB & typeC
// '&' symbol is used to combine two types which creates a new type
// with all the types of typeA, typeB and typeC combined.
type SomeA = {
a: string;
};
type SomeB = {
b: string;
};
// Now only a value having all the fields declared in SomeA
// and SomeB combined can be assigned to following declaration
type SomeTypeAAndB = SomeA & SomeB;const SomeAAndBValue: SomeTypeAAndB = {
a: "This comes from the first declaration",
b: "This comes from the second declaration"
};
// invalidconst oneMore: SomeTypeAAndB = {// invalid
d: "d is not defined in any of two types combined"
};

Genéricos

Los genéricos se utilizan para definir tipos sin asociar las declaraciones a un tipo específico. Array <T> es uno de los ejemplos de genéricos. Aquí la T denota un tipo. Implementemos un tipo para una matriz de cualquiera de los tipos escritos.

// Generics// As we know arrays are just object with numbered index and a lenght properties defined (simplified for this example)
// We can pass a type T which will be the type of the value on a variable of type CustomArray,
// except lenght, lenght is always number
interface CustomArray<T> {
[key: number]: T,
length: number,
};
// We can pass any type to the CustomArray and the passed type will act as the type of the elements on
// the declared array
const b: CustomArray<number> = [5, 3, 246, 76];const c: CustomArray<string> = ['allowed', 'allowed'];

--

--