Hace unos días anunciamos la disponibilidad de nuestro candidato de versión (RC) de TypeScript 3.4. Nuestra esperanza es recopilar comentarios y problemas iniciales para garantizar que nuestro lanzamiento final sea fácil de recoger y usar de inmediato.
Para comenzar a usar el RC, puede obtenerlo a través de NuGet o usar npm con el siguiente comando:
npm install -g typescript@rc
También puede obtener soporte del editor por
¡Exploremos las novedades en 3.4!
Este artículo en nuestro blog.Compilaciones posteriores más rápidas con la bandera --incremental
Debido a que los archivos TypeScript están compilados, introduce un paso intermedio entre escribir y ejecutar su código. Uno de nuestros objetivos es minimizar el tiempo construido dado cualquier cambio en su programa. Una forma de hacerlo es ejecutando TypeScript en modo --watch
. Cuando un archivo cambia en el modo --watch
, TypeScript puede usar el gráfico de dependencia previamente construido de su proyecto para determinar qué archivos podrían haber sido afectados y necesitan ser revisados y reemitidos. Esto puede evitar una verificación de tipo completa y una reemisión que puede ser costosa.
Pero no es realista esperar que todos los usuarios mantengan un proceso tsc --watch
se ejecuta durante la noche solo para tener versiones más rápidas mañana por la mañana. ¿Qué pasa con las construcciones frías? En los últimos meses, hemos estado trabajando para ver si hay una manera de guardar la información apropiada del modo --watch
en un archivo y usarla de compilación en compilación.
TypeScript 3.4 presenta una nueva --incremental
llamada --incremental
que le dice a TypeScript que guarde información sobre el gráfico del proyecto desde la última compilación. La próxima vez que se invoque TypeScript con --incremental
, utilizará esa información para detectar la forma menos costosa de verificar y emitir cambios en su proyecto.
De manera predeterminada con esta configuración, cuando ejecutamos tsc
, TypeScript buscará un archivo llamado .tsbuildinfo
en nuestro directorio de salida ( ./lib
). Si ./lib/.tsbuildinfo
no existe, se generará. Pero si lo hace, tsc
intentará usar ese archivo para verificar y actualizar gradualmente nuestros archivos de salida.
Estos archivos .tsbuildinfo
se pueden eliminar de forma segura y no tienen ningún impacto en nuestro código en tiempo de ejecución; se utilizan exclusivamente para hacer que las compilaciones sean más rápidas. También podemos nombrarlos como queramos y colocarlos en cualquier lugar que queramos usando el indicador --tsBuildInfoFile
.
Mientras nadie más intente escribir en el mismo archivo de caché, deberíamos poder disfrutar de compilaciones en frío incrementales más rápidas.
Proyectos compuestos
Parte de la intención con proyectos compuestos ( tsconfig.json
s con composite
establecido en true
) es que las referencias entre diferentes proyectos se pueden construir de forma incremental. Como tal, los proyectos compuestos siempre producirán archivos .tsbuildinfo
.
outFile
Cuando se usa outFile
, el nombre del archivo de información de compilación se basará en el nombre del archivo de salida. Como ejemplo, si nuestro archivo JavaScript de salida es ./output/foo.js
, entonces, bajo el indicador --incremental
, TypeScript generará el archivo ./output/foo.tsbuildinfo
. Como se --tsBuildInfoFile
anteriormente, esto se puede controlar con el indicador --tsBuildInfoFile
.
El formato de archivo --incremental
y el control de versiones
Si bien el archivo generado por --incremental
es JSON, el archivo no está destinado a ser consumido por ninguna otra herramienta. No podemos ofrecer ninguna garantía de estabilidad para su contenido y, de hecho, nuestra política actual es que cualquier versión de TypeScript no comprenderá los archivos .tsbuildinfo
generados a partir de otra versión.
Mejoras para ReadonlyArray
y readonly
ReadonlyArray
TypeScript 3.4 hace que sea un poco más fácil usar tipos de matriz de solo lectura.
Una nueva sintaxis para ReadonlyArray
El tipo ReadonlyArray
describe las ReadonlyArray
que solo se pueden leer. Cualquier variable con un identificador para un ReadonlyArray
no puede agregar, eliminar o reemplazar ningún elemento de la matriz.
function foo(arr: ReadonlyArray<string>) { arr.slice();
Si bien a menudo es una buena práctica usar ReadonlyArray
sobre Array
con el propósito de intención, a menudo ha sido una molestia dado que las matrices tienen una sintaxis más agradable. Específicamente, el number[]
es una versión abreviada de Array<number>
, al igual que Date[]
es una abreviatura de Array<Date>
.
TypeScript 3.4 presenta una nueva sintaxis para ReadonlyArray
utilizando un nuevo modificador de readonly
para tipos de matriz.
function foo(arr: readonly string[]) { arr.slice();
tuplas de readonly
TypeScript 3.4 también presenta un nuevo soporte para tuplas de readonly
. Podemos prefijar cualquier tipo de tupla con la palabra clave de readonly
para convertirla en una tupla de readonly
, al igual que ahora podemos hacer con la sintaxis abreviada de matriz. Como es de esperar, a diferencia de las tuplas ordinarias cuyas ranuras podrían escribirse, las tuplas de solo readonly
solo permiten leer desde esas posiciones.
function foo(pair: readonly [string, string]) { console.log(pair[0]);
De la misma manera que las tuplas ordinarias son tipos que se extienden desde Array
, una tupla con elementos de tipo T
1
, T
2
, ... T
n
extiende desde Array<
T
1
| T
2
| ... T
n
>
- las tuplas de readonly
son tipos que se extienden desde ReadonlyArray
. Entonces, una tupla de readonly
con los elementos T
1
, T
2
, ... T
n
extiende desde ReadonlyArray<
T
1
| T
2
| ... T
n
>
.
modificadores de tipo mapeado de readonly
readonly
y matrices de readonly
En versiones anteriores de TypeScript, generalizamos los tipos mapeados para operar de manera diferente en los tipos de matriz. Esto significaba que un tipo mapeado como Boxify
podría funcionar tanto en matrices como en tuplas.
interface Box<T> { value: T } type Boxify<T> = { [K in keyof T]: Box<T[K]> }
Desafortunadamente, los tipos mapeados como el tipo de utilidad Readonly
eran efectivamente no Readonly
los tipos de matrices y tuplas.
En TypeScript 3.4, el modificador de readonly
en un tipo mapeado convertirá automáticamente los tipos de matriz en sus correspondientes equivalentes de readonly
.
Del mismo modo, podría escribir un tipo de utilidad como Tipo mapeado de escritura que readonly
capacidad de readonly
, y que convertiría los contenedores de matriz de readonly
nuevo a sus equivalentes mutables.
type Writable<T> = { -readonly [K in keyof T]: T[K] }
Advertencias
A pesar de su apariencia, el modificador de tipo de solo readonly
solo se puede usar para sintaxis en tipos de matriz y tipos de tupla. No es un operador de tipo general.
let err1: readonly Set<number>;
afirmaciones const
Al declarar una variable o propiedad mutable, TypeScript a menudo amplía los valores para asegurarse de que podamos asignar cosas más adelante sin escribir un tipo explícito.
let x = "hello";
Técnicamente, cada valor literal tiene un tipo literal. Arriba, el tipo "hello"
se amplió a la string
tipo antes de inferir un tipo para x
.
Una vista alternativa podría ser decir que x
tiene el tipo literal original "hello"
y que no podemos asignar "world"
más adelante de esta manera:
let x: "hello" = "hello";
En este caso, eso parece extremo, pero puede ser útil en otras situaciones. Por ejemplo, los TypeScripters a menudo crean objetos destinados a ser utilizados en uniones discriminadas.
type Shape = | { kind: "circle", radius: number } | { kind: "square", sideLength: number } function getShapes(): readonly Shape[] { let result = [ { kind: "circle", radius: 100, }, { kind: "square", sideLength: 50, }, ];
La mutabilidad es una de las mejores heurísticas de intención que TypeScript puede usar para determinar cuándo ampliar (en lugar de analizar todo nuestro programa).
Desafortunadamente, como vimos en el último ejemplo, en JavaScript las propiedades son mutables por defecto. Esto significa que el lenguaje a menudo ampliará tipos indeseablemente, requiriendo tipos explícitos en ciertos lugares.
function getShapes(): readonly Shape[] {
Hasta cierto punto, esto está bien, pero a medida que nuestras estructuras de datos se vuelven cada vez más complejas, esto se vuelve engorroso.
Para resolver esto, TypeScript 3.4 introduce una nueva construcción para valores literales llamada aserciones const
. Su sintaxis es una aserción de tipo con const
en lugar del nombre de tipo (por ejemplo, 123 as const
). Cuando construimos nuevas expresiones literales con aserciones const
, podemos señalar al lenguaje que
- no se deben ampliar los tipos literales en esa expresión (por ejemplo, no pasar de
"hello"
a string
) - los literales de objeto obtienen propiedades de
readonly
- los literales de matriz se convierten en tuplas de
readonly
Fuera de los archivos .tsx
, también se puede usar la sintaxis de aserción de paréntesis angular.
Esta característica a menudo significa que los tipos que de otro modo se usarían solo para insinuar la inmutabilidad del compilador a menudo se pueden omitir.
Observe que lo anterior no necesita anotaciones de tipo. La aserción const
permitió a TypeScript tomar el tipo más específico de la expresión.
Advertencias
Una cosa a tener en cuenta es que las aserciones const
solo pueden aplicarse inmediatamente en expresiones literales simples.
Otra cosa a tener en cuenta es que los contextos const
no convierten inmediatamente una expresión para que sea completamente inmutable.
let arr = [1, 2, 3, 4]; let foo = { name: "foo", contents: arr, }; foo.name = "bar";
globalThis
para globalThis
Puede ser sorprendentemente difícil acceder o declarar valores en el ámbito global, tal vez porque estamos escribiendo nuestro código en módulos (cuyas declaraciones locales no se filtran por defecto), o porque podríamos tener una variable local que sombree el nombre de Un valor global. En diferentes entornos, hay diferentes formas de acceder a lo que efectivamente es el alcance global: global
en Node, window
, self
o frames
en el navegador, o this
en ciertas ubicaciones fuera del modo estricto. Nada de esto es obvio, y a menudo deja a los usuarios sintiéndose inseguros de si están escribiendo el código correcto.
TypeScript 3.4 presenta soporte para la verificación de tipos del nuevo globalThis
ECMAScript, una variable global que, bueno, se refiere al alcance global. A diferencia de las soluciones anteriores, globalThis
proporciona una forma estándar de acceder al alcance global que se puede utilizar en diferentes entornos.
globalThis
también es capaz de reflejar si una variable global se declaró o no const
al tratarla como una propiedad de readonly
cuando se accede a ella.
const answer = 42; globalThis.answer = 333333;
Es importante tener en cuenta que TypeScript no transforma las referencias a globalThis
al compilar versiones anteriores de ECMAScript. Como tal, a menos que esté apuntando a navegadores de hoja perenne (que ya admiten globalThis
), es posible que desee usar un polyfill apropiado en su lugar.
Convertir a parámetros con nombre
A veces, las listas de parámetros comienzan a ser difíciles de manejar.
function updateOptions( hue?: number, saturation?: number, brightness?: number, positionX?: number, positionY?: number positionZ?: number) {
En el ejemplo anterior, es demasiado fácil para una persona que llama mezclar el orden de los argumentos dados. Un patrón común de JavaScript es usar un "objeto de opciones", de modo que cada opción se nombre explícitamente y el orden nunca importe. Esto emula una característica que otros idiomas han llamado "parámetros con nombre".
interface Options { hue?: number, saturation?: number, brightness?: number, positionX?: number, positionY?: number positionZ?: number } function updateOptions(options: Options = {}) {
El equipo de TypeScript no solo trabaja en un compilador, también brindamos la funcionalidad que los editores usan para funciones ricas como terminaciones, definición y refactorizaciones. En TypeScript 3.4, nuestra pasante Gabriela Britto ha implementado una nueva refactorización para convertir las funciones existentes para usar este patrón de "parámetros con nombre".

Si bien podemos cambiar el nombre de la función en nuestra versión 3.4 final y creemos que puede haber espacio para parte de la ergonomía, nos encantaría que pruebe la función y nos envíe sus comentarios.
Rompiendo cambios
Nivel superior this
ahora está escrito
El tipo de nivel superior ahora se escribe como typeof globalThis
lugar de any
. Como consecuencia, puede recibir errores por acceder a valores desconocidos en this
bajo noImplicitAny
.
Tenga en cuenta que el código compilado bajo noImplicitThis
no experimentará ningún cambio aquí.
Argumentos propagados de tipo genérico
En ciertos casos, la inferencia mejorada de TypeScript 3.4 puede producir funciones que son genéricas, en lugar de las que toman y devuelven sus restricciones (generalmente {}
).
declare function compose<T, U, V>(f: (arg: T) => U, g: (arg: U) => V): (arg: T) => V; function list<T>(x: T) { return [x]; } function box<T>(value: T) { return { value }; } let f = compose(list, box); let x = f(100)
Una anotación de tipo explícito en x
puede eliminar el error.
Que sigue
TypeScript 3.4 es nuestra primera versión que ha tenido un plan de iteración que describe nuestros planes para esta versión, que está destinado a alinearse con nuestra hoja de ruta de 6 meses . Puede estar atento a ambos y a nuestra página de hoja de ruta de funciones para cualquier próximo trabajo.
En este momento esperamos escuchar su experiencia con el RC, ¡así que pruébelo ahora y díganos qué piensa!