Índice
- Mecanografía básica
- Conjuntos más pequeños
- Introduzca genéricos
- Escribiendo parámetros
- Enlaces genéricos
- Tipos condicionales y tipos literales de plantilla recursiva
- Tipos estáticos para comportamiento dinámico
En este artículo, analizamos algunas de las características más avanzadas de TypeScript, como tipos de unión, tipos condicionales, tipos literales de plantilla y genéricos. Queremos formalizar el comportamiento más dinámico de JavaScript de manera que podamos detectar la mayoría de los errores antes de que ocurran. Aplicamos varios aprendizajes de todos los capítulos de TypeScript en 50 lecciones , un libro que publicamos aquí en Smashing Magazine a fines de 2020. Si está interesado en aprender más, ¡asegúrese de consultarlo!
JavaScript es un lenguaje de programación inherentemente dinámico. Nosotros, como desarrolladores, podemos expresar mucho con poco esfuerzo, y el lenguaje y su tiempo de ejecución determinan lo que pretendemos hacer. ¡Esto es lo que hace que JavaScript sea tan popular entre los principiantes y lo que hace que los desarrolladores experimentados sean productivos! Sin embargo, hay una advertencia: ¡debemos estar alerta! Errores, errores tipográficos, comportamiento correcto del programa: ¡mucho de eso sucede en nuestra cabeza!
Eche un vistazo al siguiente ejemplo.
app.get("/api/users/:userID", function(req, res) { if (req.method === "POST") { res.status(20).send({ message: "Got you, user " + req.params.userId }); }})
Tenemos un servidor estilo https://expressjs.com/ que nos permite definir una ruta (o ruta) y ejecuta una devolución de llamada si se solicita la URL.
La devolución de llamada toma dos argumentos:
- El
request
objeto.
Aquí obtenemos información sobre el método HTTP utilizado (por ejemplo, GET, POST, PUT, DELETE) y parámetros adicionales que entran. En este ejemplo,userID
se debe asignar a un parámetrouserID
que, bueno, contenga el ID del usuario. - El objeto
response
ureply
.
Aquí queremos preparar una respuesta adecuada del servidor al cliente. Queremos enviar códigos de estado correctos (métodostatus
) y enviar salida JSON por cable.
Lo que vemos en este ejemplo está muy simplificado, pero da una buena idea de lo que estamos haciendo. ¡El ejemplo anterior también está plagado de errores! Echar un vistazo:
app.get("/api/users/:userID", function(req, res) { if (req.method === "POST") { /* Error 1 */ res.status(20).send({ /* Error 2 */ message: "Welcome, user " + req.params.userId /* Error 3 */ }); }})
¡Oh, vaya! ¿Tres líneas de código de implementación y tres errores? ¿Lo que ha sucedido?
- El primer error tiene matices. Si bien le decimos a nuestra aplicación que queremos escuchar solicitudes GET
app.get
(por lo tanto ), solo hacemos algo si el método de solicitud es POST . En este punto particular de nuestra aplicación,req.method
no se puede POST . Por lo tanto, nunca enviaríamos ninguna respuesta, lo que podría provocar tiempos de espera inesperados. - ¡Genial que enviemos explícitamente un código de estado!
20
Sin embargo, no es un código de estado válido. Es posible que los clientes no entiendan lo que está sucediendo aquí. - Esta es la respuesta que queremos enviar. Accedemos a los argumentos analizados pero tenemos un error tipográfico grave. Que
userID
no esuserId
. Todos nuestros usuarios serán recibidos con un "¡Bienvenido, usuario indefinido!". ¡Algo que definitivamente has visto en la naturaleza!
¡Y cosas así suceden! Especialmente en JavaScript. Ganamos expresividad (nunca tuvimos que preocuparnos por los tipos), pero tenemos que prestar mucha atención a lo que estamos haciendo.
Aquí también es donde JavaScript recibe muchas reacciones negativas por parte de los programadores que no están acostumbrados a los lenguajes de programación dinámicos. Por lo general, tienen compiladores que les señalan posibles problemas y detectan errores por adelantado. Es posible que parezcan presumidos cuando desaprueban la cantidad de trabajo adicional que tienes que hacer en tu cabeza para asegurarte de que todo funcione bien. Incluso podrían decirte que JavaScript no tiene tipos. Lo cual no es cierto.
Anders Hejlsberg, el arquitecto principal de TypeScript, dijo en su discurso de apertura de MS Build 2017 que “ no es que JavaScript no tenga un sistema de tipos. Simplemente no hay manera de formalizarlo ”.
Y este es el objetivo principal de TypeScript. TypeScript quiere comprender su código JavaScript mejor que usted. Y cuando TypeScript no pueda entender lo que quiere decir, puede ayudar proporcionando información de tipo adicional.
Mecanografía básica
Y esto es lo que vamos a hacer ahora. Tomemos el get
método de nuestro servidor estilo Express y agreguemos suficiente información de tipo para poder excluir tantas categorías de errores como sea posible.
Comenzamos con información de tipo básico. Tenemos un app
objeto que apunta a una get
función. La get
función toma path
, que es una cadena, y una devolución de llamada.
const app = { get, /* post, put, delete, ... to come! */};function get(path: string, callback: CallbackFn) { // to be implemented -- not important right now}
Si bien string
es un tipo básico, llamado primitivo , CallbackFn
es un tipo compuesto que tenemos que definir explícitamente.
CallbackFn
es un tipo de función que toma dos argumentos:
req
, que es de tipoServerRequest
reply
que es de tipoServerReply
CallbackFn
devoluciones void
.
type CallbackFn = (req: ServerRequest, reply: ServerReply) = void;
ServerRequest
es un objeto bastante complejo en la mayoría de los marcos. Hacemos una versión simplificada con fines de demostración. Pasamos una method
cadena, for "GET"
, "POST"
, "PUT"
, "DELETE"
, etc. También tiene un params
registro. Los registros son objetos que asocian un conjunto de claves con un conjunto de propiedades. Por ahora, queremos permitir que cada string
clave se asigne a una string
propiedad. Refactorizamos este más tarde.
type ServerRequest = { method: string; params: Recordstring, string;};
Para ServerReply
, presentamos algunas funciones, sabiendo que un ServerReply
objeto real tiene muchas más. Una send
función toma un argumento opcional con los datos que queremos enviar. Y tenemos la posibilidad de establecer un código de estado con la status
función.
type ServerReply = { send: (obj?: any) = void; status: (statusCode: number) = ServerReply;};
Eso ya es algo, y podemos descartar un par de errores:
app.get("/api/users/:userID", function(req, res) { if(req.method === 2) {// ^^^^^^^^^^^^^^^^^ Error, type number is not assignable to string res.status("200").send()// ^^^^^ Error, type string is not assignable to number }})
Pero aún podemos enviar códigos de estado incorrectos (cualquier número es posible) y no tener idea de los posibles métodos HTTP (cualquier cadena es posible). Refinamos nuestros tipos.
Conjuntos más pequeños
Puede ver los tipos primitivos como un conjunto de todos los valores posibles de esa categoría determinada. Por ejemplo, string
incluye todas las cadenas posibles que se pueden expresar en JavaScript, number
incluye todos los números posibles con precisión de doble flotante. boolean
incluye todos los valores booleanos posibles, que son true
y false
.
TypeScript le permite refinar esos conjuntos a subconjuntos más pequeños. Por ejemplo, podemos crear un tipo Method
que incluya todas las cadenas posibles que podemos recibir para los métodos HTTP:
type Methods= "GET" | "POST" | "PUT" | "DELETE";type ServerRequest = { method: Methods; params: Recordstring, string;};
Method
es un conjunto más pequeño del string
conjunto más grande. Method
es un tipo de unión de tipos literales. Un tipo literal es la unidad más pequeña de un conjunto dado. Una cadena literal. Un número literal. No hay ambigüedad. Es solo "GET"
. Los colocas en una unión con otros tipos literales, creando un subconjunto de los tipos más grandes que tengas. También puede crear un subconjunto con tipos literales de ambos string
y number
, o diferentes tipos de objetos compuestos. Hay muchas posibilidades para combinar y unir tipos literales.
Esto tiene un efecto inmediato en la devolución de llamada de nuestro servidor. De repente, podemos diferenciar entre esos cuatro métodos (o más si es necesario) y podemos agotar todas las posibilidades en el código. TypeScript nos guiará:
app.get("/api/users/:userID", function (req, res) { // at this point, TypeScript knows that req.method // can take one of four possible values switch (req.method) { case "GET": break; case "POST": break; case "DELETE": break; case "PUT": break; default: // here, req.method is never req.method; }});
Con cada case
declaración que haga, TypeScript puede brindarle información sobre las opciones disponibles. Pruébelo usted mismo . Si agotó todas las opciones, TypeScript le dirá en su default
sucursal que esto puede never
suceder. Este es literalmente el tipo never
, lo que significa que posiblemente haya alcanzado un estado de error que necesita manejar.
Esa es una categoría de errores menos. Ahora sabemos exactamente qué posibles métodos HTTP están disponibles.
Podemos hacer lo mismo con los códigos de estado HTTP, definiendo un subconjunto de números válidos que statusCode
pueden tomar:
type StatusCode = 100 | 101 | 102 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 444 | 449 | 450 | 451 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 598 | 599;type ServerReply = { send: (obj?: any) = void; status: (statusCode: StatusCode) = ServerReply;};
El tipo StatusCode
es nuevamente un tipo de unión. Y con esto excluimos otra categoría de errores. De repente, un código como ese falla:
app.get("/api/user/:userID", (req, res) = { if(req.method === "POS") {// ^^^^^^^^^^^^^^^^^^^ 'Methods' and '"POS"' have no overlap. res.status(20)// ^^ '20' is not assignable to parameter of type 'StatusCode' }})
¡Y nuestro software se vuelve mucho más seguro! ¡Pero podemos hacer más!
Introduzca genéricos
Cuando definimos una ruta con app.get
, implícitamente sabemos que el único método HTTP posible es "GET"
. Pero con nuestras definiciones de tipos, todavía tenemos que verificar todas las partes posibles de la unión.
El tipo for CallbackFn
es correcto, ya que podríamos definir funciones de devolución de llamada para todos los métodos HTTP posibles, pero si llamamos explícitamente a app.get
, sería bueno ahorrar algunos pasos adicionales que solo son necesarios para cumplir con los tipos.
¡Los genéricos de TypeScript pueden ayudar! Los genéricos son una de las características principales de TypeScript que le permiten obtener el comportamiento más dinámico de los tipos estáticos. En TypeScript en 50 lecciones , dedicamos los últimos tres capítulos a profundizar en todas las complejidades de los genéricos y su funcionalidad única.
Lo que necesita saber ahora es que queremos definir ServerRequest
de manera que podamos especificar una parte Methods
en lugar de todo el conjunto. Para eso usamos la sintaxis genérica donde podemos definir parámetros como lo haríamos con las funciones:
type ServerRequestMet extends Methods = { method: Met; params: Recordstring, string;};
Esto es lo que pasa:
ServerRequest
se convierte en un tipo genérico, como lo indican los corchetes angulares- Definimos un parámetro genérico llamado
Met
, que es un subconjunto de tipoMethods
- Usamos este parámetro genérico como variable genérica para definir el método.
También te recomiendo que consultes mi artículo sobre cómo nombrar parámetros genéricos .
Con ese cambio, podemos especificar diferentes ServerRequest
mensajes sin duplicar cosas:
type OnlyGET = ServerRequest"GET";type OnlyPOST = ServerRequest"POST";type POSTorPUT = ServerRquest"POST" | "PUT";
Dado que cambiamos la interfaz de ServerRequest
, tenemos que realizar cambios en todos los demás tipos que usan ServerRequest
, me gusta CallbackFn
y la get
función:
type CallbackFnMet extends Methods = ( req: ServerRequestMet, reply: ServerReply) = void;function get(path: string, callback: CallbackFn"GET") { // to be implemented}
Con la get
función, pasamos un argumento real a nuestro tipo genérico. Sabemos que no será solo un subconjunto de Methods
, sabemos exactamente con qué subconjunto estamos tratando.
Ahora, cuando usamos app.get
, solo tenemos un valor posible para req.method
:
app.get("/api/users/:userID", function (req, res) { req.method; // can only be get});
Esto garantiza que no asumimos que métodos HTTP similares "POST"
o similares estén disponibles cuando creamos una app.get
devolución de llamada. Sabemos exactamente a qué nos enfrentamos en este momento, así que reflejémoslo en nuestros tipos.
Ya hicimos mucho para asegurarnos de que request.method
esté escrito de manera razonable y represente el estado real de las cosas. Un buen beneficio que obtenemos al subconjunto del Methods
tipo de unión es que podemos crear una función de devolución de llamada de propósito general fuera de app.get
la que sea segura para tipos:
const handler: CallbackFn"PUT" | "POST" = function(res, req) { res.method // can be "POST" or "PUT"};const handlerForAllMethods: CallbackFnMethods = function(res, req) { res.method // can be all methods};app.get("/api", handler);// ^^^^^^^ Nope, we don’t handle "GET"app.get("/api", handlerForAllMethods); // This works
Escribiendo parámetros
Lo que no hemos tocado todavía es escribir el params
objeto. Hasta ahora, obtenemos un registro que permite acceder a todas string
las claves. ¡Nuestra tarea ahora es hacerlo un poco más específico!
Lo hacemos agregando otra variable genérica. Uno para los métodos, otro para las posibles claves en nuestro Record
:
type ServerRequestMet extends Methods, Par extends string = string = { method: Met; params: RecordPar, string;};
La variable de tipo genérico Par
puede ser un subconjunto de tipo string
y el valor predeterminado es cada cadena. Con eso, podemos saber ServerRequest
qué claves esperamos:
// request.method = "GET"// request.params = {// userID: string// }type WithUserID = ServerRequest"GET", "userID"
Agreguemos el nuevo argumento a nuestra get
función y el CallbackFn
tipo, para que podamos configurar los parámetros solicitados:
function getPar extends string = string( path: string, callback: CallbackFn"GET", Par) { // to be implemented}type CallbackFnMet extends Methods, Par extends string = ( req: ServerRequestMet, Par, reply: ServerReply) = void;
Si no lo configuramos Par
explícitamente, el tipo funciona como estamos acostumbrados, ya que Par
el valor predeterminado es string
. Sin embargo, si lo configuramos, ¡de repente tendremos una definición adecuada para el req.params
objeto!
app.get"userID"("/api/users/:userID", function (req, res) { req.params.userID; // Works!! req.params.anythingElse; // doesn’t work!!});
¡Genial! Aunque hay una pequeña cosa que se puede mejorar. Todavía podemos pasar cada cadena al path
argumento de app.get
. ¿No sería mejor si pudiéramos reflexionar Par
allí también?
¡Podemos! Con el lanzamiento de la versión 4.1, TypeScript puede crear tipos literales de plantilla . Sintácticamente, funcionan igual que los literales de plantilla de cadena, pero a nivel de tipo. Mientras pudimos dividir el conjunto string
en subconjuntos con tipos literales de cadena (como lo hicimos con Métodos), los tipos literales de plantilla nos permiten incluir un espectro completo de cadenas.
Creemos un tipo llamado IncludesRouteParams
, donde queremos asegurarnos de que Par
esté incluido correctamente en la forma estilo Express de agregar dos puntos delante del nombre del parámetro:
type IncludesRouteParamsPar extends string = | `${string}/:${Par}` | `${string}/:${Par}/${string}`;
El tipo genérico IncludesRouteParams
toma un argumento, que es un subconjunto de string
. Crea un tipo de unión de dos literales de plantilla:
- El primer literal de plantilla comienza con cualquiera
string
y luego incluye un/
carácter seguido de un:
carácter, seguido del nombre del parámetro. Esto asegura que detectemos todos los casos en los que el parámetro está al final de la cadena de ruta. - El segundo literal de plantilla comienza con any
string
, seguido del mismo patrón de/
y:
el nombre del parámetro. Luego tenemos otro/
carácter, seguido de cualquier cadena. Esta rama del tipo unión asegura que detectemos todos los casos en los que el parámetro se encuentra en algún lugar dentro de una ruta.
Así es como se comporta IncludesRouteParams
el nombre del parámetro userID
con diferentes casos de prueba:
const a: IncludeRouteParams"userID" = "/api/user/:userID" // const a: IncludeRouteParams"userID" = "/api/user/:userID/orders" // const a: IncludeRouteParams"userID" = "/api/user/:userId" // const a: IncludeRouteParams"userID" = "/api/user" // const a: IncludeRouteParams"userID" = "/api/user/:userIDAndmore" //
Incluyamos nuestro nuevo tipo de utilidad en la get
declaración de función.
function getPar extends string = string( path: IncludesRouteParamsPar, callback: CallbackFn"GET", Par) { // to be implemented}app.get"userID"( "/api/users/:userID", function (req, res) { req.params.userID; // YEAH! });
¡Excelente! ¡Tenemos otro mecanismo de seguridad para garantizar que no nos perdamos de agregar los parámetros a la ruta real! Qué poderoso.
Enlaces genéricos
Pero adivina qué, todavía no estoy contento con eso. Hay algunos problemas con ese enfoque que se vuelven evidentes en el momento en que sus rutas se vuelven un poco más complejas.
- El primer problema que tengo es que necesitamos indicar explícitamente nuestros parámetros en el parámetro de tipo genérico. Tenemos que vincularnos
Par
a"userID"
, aunque lo especificaríamos de todos modos en el argumento de ruta de la función. ¡Esto no es JavaScript! - Este enfoque solo maneja un parámetro de ruta. En el momento en que agregamos una unión, por ejemplo,
"userID" | "orderId"
la verificación de seguridad se cumple con solo uno de esos argumentos disponible. Así funcionan los decorados. Puede ser uno o el otro.
Debe haber una mejor manera. Y ahí está. De lo contrario, este artículo terminaría con una nota muy amarga.
¡Inviertamos el orden! No intentemos definir los parámetros de ruta en una variable de tipo genérico, sino más bien extraer las variables de las que path
pasamos como primer argumento de app.get
.
Para llegar al valor real, tenemos que ver cómo funciona el enlace genérico en TypeScript. Tomemos esta identity
función por ejemplo:
function identityT(inp: T) : T { return inp}
Puede que sea la función genérica más aburrida que jamás hayas visto, pero ilustra perfectamente un punto. identity
toma un argumento y devuelve la misma entrada nuevamente. El tipo es el tipo genérico T
y también devuelve el mismo tipo.
Ahora podemos vincularnos T
a string
, por ejemplo:
const z = identitystring("yes"); // z is of type string
Este enlace explícitamente genérico garantiza que solo pasemos strings
a identity
y, dado que enlazamos explícitamente, el tipo de retorno también es string
. Si nos olvidamos de enlazar, sucede algo interesante:
const y = identity("yes") // y is of type "yes"
En ese caso, TypeScript infiere el tipo a partir del argumento que pasa y lo vincula T
al tipo literal de cadena "yes"
. Esta es una excelente manera de convertir un argumento de función a un tipo literal, que luego usamos en nuestros otros tipos genéricos.
Hagámoslo adaptándonos app.get
.
function getPath extends string = string( path: Path, callback: CallbackFn"GET", ParseRouteParamsPath) { // to be implemented}
Eliminamos el Par
tipo genérico y añadimos Path
. Path
puede ser un subconjunto de cualquiera string
. Lo configuramos path
en este tipo genérico Path
, lo que significa que en el momento en que le pasamos un parámetro get
, capturamos su tipo literal de cadena. Pasamos Path
a un nuevo tipo genérico ParseRouteParams
que aún no hemos creado.
Trabajemos en ParseRouteParams
. Aquí, volvemos a cambiar el orden de los eventos. En lugar de pasar los parámetros de ruta solicitados al genérico para asegurarnos de que la ruta esté bien, pasamos la ruta de ruta y extraemos los posibles parámetros de ruta. Para eso, necesitamos crear un tipo condicional.
Tipos condicionales y tipos literales de plantilla recursiva
Los tipos condicionales son sintácticamente similares al operador ternario en JavaScript. Verifica una condición y, si se cumple, devuelve la rama A; de lo contrario, devuelve la rama B. Por ejemplo:
type ParseRouteParamsRte = Rte extends `${string}/:${infer P}` ? P : never;
Aquí, verificamos si Rte
hay un subconjunto de cada ruta que termina con el parámetro al final Estilo Express (con un precedente "/:"
). Si es así, inferimos esta cadena. Lo que significa que capturamos su contenido en una nueva variable. Si se cumple la condición, devolvemos la cadena recién extraída; de lo contrario, devolvemos nunca, como en: "No hay parámetros de ruta".
Si lo probamos, obtenemos algo como esto:
type Params = ParseRouteParams"/api/user/:userID" // Params is "userID"type NoParams = ParseRouteParams"/api/user" // NoParams is never -- no params!
Genial, eso ya es mucho mejor que lo que hicimos antes. Ahora queremos capturar todos los demás parámetros posibles. Para eso tenemos que agregar otra condición:
type ParseRouteParamsRte = Rte extends `${string}/:${infer P}/${infer Rest}` ? P | ParseRouteParams`/${Rest}` : Rte extends `${string}/:${infer P}` ? P : never;
Nuestro tipo condicional funciona ahora de la siguiente manera:
- En la primera condición, verificamos si hay un parámetro de ruta en algún lugar entre la ruta. Si es así, extraemos tanto el parámetro de ruta como todo lo que viene después. Devolvemos el parámetro de ruta recién encontrado
P
en una unión donde llamamos al mismo tipo genérico de forma recursiva con el archivoRest
. Por ejemplo, si pasamos la ruta"/api/users/:userID/orders/:orderID"
aParseRouteParams
, inferimos"userID"
haciaP
y"orders/:orderID"
haciaRest
. Llamamos al mismo tipo conRest
- Aquí es donde entra en juego la segunda condición. Aquí comprobamos si hay un tipo al final. Este es el caso de
"orders/:orderID"
. Extraemos"orderID"
y devolvemos este tipo literal. - Si no queda más parámetro de ruta, regresamos nunca.
Dan Vanderkam muestra un tipo similar y más elaborado para ParseRouteParams
, pero el que ves arriba también debería funcionar. Si probamos nuestro recién adaptado ParseRouteParams
, obtenemos algo como esto:
// Params is "userID"type Params = ParseRouteParams"/api/user/:userID"// MoreParams is "userID" | "orderID"type MoreParams = ParseRouteParams"/api/user/:userID/orders/:orderId"
Apliquemos este nuevo tipo y veamos cómo app.get
se ve nuestro uso final.
app.get("/api/users/:userID/orders/:orderID", function (req, res) { req.params.userID; // YES!! req.params.orderID; // Also YES!!!});
Guau. ¡Eso se parece al código JavaScript que teníamos al principio!
Tipos estáticos para comportamiento dinámico
Los tipos que acabamos de crear para una función app.get
aseguran que excluimos un montón de posibles errores:
- Sólo podemos pasar códigos de estado numéricos adecuados a
res.status()
req.method
es una de las cuatro cadenas posibles, y cuando usamosapp.get
, sabemos que solo será"GET"
- Podemos analizar los parámetros de ruta y asegurarnos de que no tengamos ningún error tipográfico dentro de nuestra devolución de llamada.
Si miramos el ejemplo del principio de este artículo, obtenemos los siguientes mensajes de error:
app.get("/api/users/:userID", function(req, res) { if (req.method === "POST") {// ^^^^^^^^^^^^^^^^^^^^^// This condition will always return 'false'// since the types '"GET"' and '"POST"' have no overlap. res.status(20).send({// ^^// Argument of type '20' is not assignable to// parameter of type 'StatusCode' message: "Welcome, user " + req.params.userId// ^^^^^^// Property 'userId' does not exist on type// '{ userID: string; }'. Did you mean 'userID'? }); }})
¡Y todo eso antes de ejecutar nuestro código! Los servidores estilo Express son un ejemplo perfecto de la naturaleza dinámica de JavaScript. Dependiendo del método que llame, la cadena que pase como primer argumento, muchos cambios de comportamiento dentro de la devolución de llamada. Tome otro ejemplo y todos sus tipos se verán completamente diferentes.
Pero con algunos tipos bien definidos, podemos captar este comportamiento dinámico mientras editamos nuestro código. ¡En tiempo de compilación con tipos estáticos, no en tiempo de ejecución cuando las cosas van bien!
Y este es el poder de TypeScript. Un sistema de tipos estáticos que intenta formalizar todo el comportamiento dinámico de JavaScript que todos conocemos tan bien. Si quieres probar el ejemplo que acabamos de crear, dirígete al área de juegos de TypeScript y juguetea con él.
En este artículo, abordamos muchos conceptos. Si desea saber más, consulte TypeScript en 50 lecciones , donde obtendrá una breve introducción al sistema de tipos en lecciones pequeñas y fácilmente digeribles. Las versiones de libros electrónicos están disponibles de inmediato y el libro impreso será una excelente referencia para su biblioteca de codificación.
(vf, il)Explora más en
- Herramientas
- Codificación
- javascript
- Mecanografiado
Tal vez te puede interesar:
- ¿Deberían abrirse los enlaces en ventanas nuevas?
- 24 excelentes tutoriales de AJAX
- 70 técnicas nuevas y útiles de AJAX y JavaScript
- Más de 45 excelentes recursos y repositorios de fragmentos de código
Escritura estática dinámica en TypeScript
Mecanografía básicaConjuntos más pequeñosIntroduzca genéricosEscribiendo parámetrosEnlaces genéricosTipos condicionales y tipos literales de plantilla r
programar
es
2025-01-15
Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente