Anúncio do TypeScript 3.4 RC

Alguns dias atrás, anunciamos a disponibilidade do nosso candidato a lançamento (RC) do TypeScript 3.4. Nossa esperança é coletar comentários e questões iniciais para garantir que nosso lançamento final seja fácil de usar e usar imediatamente.


Para começar a usar o RC, você pode acessá- lo através do NuGet ou usar o npm com o seguinte comando:


npm install -g typescript@rc 

Você também pode obter suporte do editor



Vamos explorar o que há de novo no 3.4!




Este artigo em nosso blog.

Construções subsequentes mais rápidas com o sinalizador --incremental


Como os arquivos TypeScript são compilados, ele apresenta uma etapa intermediária entre escrever e executar seu código. Um dos nossos objetivos é minimizar o tempo de construção, dada qualquer alteração no seu programa. Uma maneira de fazer isso é executando o TypeScript no modo --watch . Quando um arquivo é alterado no modo --watch , o TypeScript pode usar o gráfico de dependência do seu projeto para determinar quais arquivos podem ter sido afetados e precisam ser verificados novamente e reemitidos. Isso pode evitar uma verificação completa do tipo e reemitir, o que pode ser caro.


Mas não é realista esperar que todos os usuários mantenham um processo tsc --watch execução durante a noite apenas para ter compilações mais rápidas amanhã de manhã. E as construções a frio? Nos últimos meses, trabalhamos para verificar se há uma maneira de salvar as informações apropriadas do modo --watch em um arquivo e usá-las de compilação para compilação.


O TypeScript 3.4 apresenta um novo sinalizador chamado --incremental que informa ao TypeScript para salvar informações sobre o gráfico do projeto desde a última compilação. Na próxima vez que o TypeScript for chamado com --incremental , ele usará essas informações para detectar a maneira mais barata de verificar o tipo e emitir alterações em seu projeto.


 // tsconfig.json { "compilerOptions": { "incremental": true, "outDir": "./lib" }, "include": ["./src"] } 

Por padrão, com essas configurações, quando executamos o tsc, o TypeScript procurará um arquivo chamado .tsbuildinfo em nosso diretório de saída ( ./lib ). Se ./lib/.tsbuildinfo não existir, será gerado. Mas, se o fizer, o tsc tentará usar esse arquivo para verificar incrementalmente o tipo e atualizar nossos arquivos de saída.


Esses arquivos .tsbuildinfo podem ser excluídos com segurança e não afetam nosso código em tempo de execução - eles são puramente usados ​​para tornar as compilações mais rápidas. Também podemos nomear o que quisermos e colocá-lo em qualquer lugar que quisermos usando o sinalizador --tsBuildInfoFile .


 // front-end.tsconfig.json { "compilerOptions": { "incremental": true, "tsBuildInfoFile": "./buildcache/front-end", "outDir": "./lib" }, "include": ["./src"] } 

Desde que ninguém mais tente gravar no mesmo arquivo de cache, poderemos desfrutar de compilações a frio incrementais mais rápidas.


Projetos compostos


Parte da intenção com projetos compostos ( tsconfig.json s com composite definido como true ) é que as referências entre diferentes projetos podem ser construídas de forma incremental. Dessa forma, os projetos compostos sempre produzirão arquivos .tsbuildinfo .


outFile


Quando outFile é usado, o nome do arquivo de informações de construção será baseado no nome do arquivo de saída. Por exemplo, se nosso arquivo JavaScript de saída for ./output/foo.js , sob o sinalizador --incremental , o TypeScript gerará o arquivo ./output/foo.tsbuildinfo . Como acima, isso pode ser controlado com o sinalizador --tsBuildInfoFile .


O formato de arquivo --incremental e a versão


Embora o arquivo gerado por --incremental seja JSON, o arquivo não deve ser consumido por nenhuma outra ferramenta. Não podemos fornecer garantias de estabilidade para seu conteúdo e, de fato, nossa política atual é que qualquer versão do TypeScript não entenda os arquivos .tsbuildinfo gerados a partir de outra versão.


Melhorias para as tuplas ReadonlyArray e readonly


O TypeScript 3.4 facilita um pouco o uso de tipos de matriz somente leitura.


Uma nova sintaxe para o ReadonlyArray


O tipo ReadonlyArray descreve as Array que só podem ser lidas. Qualquer variável com um identificador para um ReadonlyArray não pode adicionar, remover ou substituir nenhum elemento da matriz.


 function foo(arr: ReadonlyArray<string>) { arr.slice(); // okay arr.push("hello!"); // error! } 

Embora muitas vezes seja uma boa prática usar o ReadonlyArray sobre Array para fins de intenção, muitas vezes tem sido uma dor, uma vez que as matrizes têm uma sintaxe mais agradável. Especificamente, o number[] é uma versão abreviada da Array<number> , assim como a Date[] é uma abreviação da Array<Date> .


O TypeScript 3.4 introduz uma nova sintaxe para o ReadonlyArray usando um novo modificador readonly para tipos de array.


 function foo(arr: readonly string[]) { arr.slice(); // okay arr.push("hello!"); // error! } 

tuplas readonly


O TypeScript 3.4 também apresenta novo suporte para tuplas readonly . Podemos prefixar qualquer tipo de tupla com a palavra-chave readonly para torná-la uma tupla readonly , assim como podemos agora com a sintaxe abreviada da matriz. Como você pode esperar, diferentemente das tuplas comuns, nas quais os slots podem ser gravados, as tuplas somente leitura permitem a leitura dessas posições.


 function foo(pair: readonly [string, string]) { console.log(pair[0]); // okay pair[1] = "hello!"; // error } 

Da mesma forma que tuplas comuns são tipos que se estendem da Array - uma tupla com elementos do tipo T 1 , T , ... n se estende da Array< T 1 | T 2 ... T n > - tuplas readonly são tipos que se estendem do ReadonlyArray . Portanto, uma tupla readonly com elementos T 1 , T 2 , ... T n se estende de ReadonlyArray< T 1 | T 2 ... T n > .


modificadores de tipo mapeados readonly readonly e matrizes readonly


Nas versões anteriores do TypeScript, generalizamos os tipos mapeados para operar de maneira diferente nos tipos de matriz. Isso significava que um tipo mapeado como o Boxify poderia funcionar em matrizes e tuplas.


 interface Box<T> { value: T } type Boxify<T> = { [K in keyof T]: Box<T[K]> } // { a: Box<string>, b: Box<number> } type A = Boxify<{ a: string, b: number }>; // Array<Box<number>> type B = Boxify<number[]>; // [Box<string>, Box<number>] type C = Boxify<[string, boolean]>; 

Infelizmente, tipos mapeados como o tipo de utilitário Readonly eram efetivamente não ops nos tipos de matriz e tupla.


 // lib.d.ts type Readonly<T> = { readonly [K in keyof T]: T[K] } // How code acted *before* TypeScript 3.4 // { readonly a: string, readonly b: number } type A = Readonly<{ a: string, b: number }>; // number[] type B = Readonly<number[]>; // [string, boolean] type C = Readonly<[string, boolean]>; 

No TypeScript 3.4, o modificador readonly em um tipo mapeado converte automaticamente tipos semelhantes a matrizes em seus correspondentes readonly correspondentes.


 // How code acts now *with* TypeScript 3.4 // { readonly a: string, readonly b: number } type A = Readonly<{ a: string, b: number }>; // readonly number[] type B = Readonly<number[]>; // readonly [string, boolean] type C = Readonly<[string, boolean]>; 

Da mesma forma, você pode escrever um tipo de utilitário como o tipo mapeado Writable que retira o readonly -ness e converte os contêineres do array readonly volta aos seus equivalentes mutáveis.


 type Writable<T> = { -readonly [K in keyof T]: T[K] } // { a: string, b: number } type A = Writable<{ readonly a: string; readonly b: number }>; // number[] type B = Writable<readonly number[]>; // [string, boolean] type C = Writable<readonly [string, boolean]>; 

Advertências


Apesar de sua aparência, o modificador de tipo readonly pode ser usado para sintaxe em tipos de matriz e tupla. Não é um operador do tipo de uso geral.


 let err1: readonly Set<number>; // error! let err2: readonly Array<boolean>; // error! let okay: readonly boolean[]; // works fine 

afirmações const


Ao declarar uma variável ou propriedade mutável, o TypeScript geralmente amplia os valores para garantir que possamos atribuir coisas posteriormente, sem escrever um tipo explícito.


 let x = "hello"; // hurray! we can assign to 'x' later on! x = "world"; 

Tecnicamente, todo valor literal tem um tipo literal. Acima, o tipo "hello" foi ampliado para a string tipos antes de inferir um tipo para x .


Uma visão alternativa pode ser dizer que x tem o tipo literal original "hello" e que não podemos atribuir "world" posteriormente assim:


 let x: "hello" = "hello"; // error! x = "world"; 

Nesse caso, isso parece extremo, mas pode ser útil em outras situações. Por exemplo, os TypeScripters geralmente criam objetos que devem ser usados ​​em uniões 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, }, ]; // Some terrible error message because TypeScript inferred // 'kind' to have the type 'string' instead of // either '"circle"' or '"square"'. return result; } 

A mutabilidade é uma das melhores heurísticas de intenção que o TypeScript pode usar para determinar quando ampliar (em vez de analisar todo o programa).


Infelizmente, como vimos no último exemplo, no JavaScript, as propriedades são mutáveis ​​por padrão. Isso significa que o idioma geralmente alarga tipos indesejáveis, exigindo tipos explícitos em determinados lugares.


 function getShapes(): readonly Shape[] { // This explicit annotation gives a hint // to avoid widening in the first place. let result: readonly Shape[] = [ { kind: "circle", radius: 100, }, { kind: "square", sideLength: 50, }, ]; return result; } 

Até certo ponto, tudo bem, mas, conforme nossas estruturas de dados se tornam cada vez mais complexas, isso se torna complicado.


Para resolver isso, o TypeScript 3.4 apresenta uma nova construção para valores literais chamados const assertions. Sua sintaxe é uma asserção de tipo com const no lugar do nome do tipo (por exemplo, 123 as const ). Quando construímos novas expressões literais com afirmações const , podemos sinalizar para a linguagem que


  • nenhum tipo literal nessa expressão deve ser ampliado (por exemplo, não é possível passar de "hello" para string )
  • literais de objeto obtêm propriedades readonly
  • literais de matriz tornam-se tuplas readonly

 // Type '10' let x = 10 as const; // Type 'readonly [10, 20]' let y = [10, 20] as const; // Type '{ readonly text: "hello" }' let z = { text: "hello" } as const; 

Fora dos arquivos .tsx , a sintaxe da asserção de colchetes angulares também pode ser usada.


 // Type '10' let x = <const>10; // Type 'readonly [10, 20]' let y = <const>[10, 20]; // Type '{ readonly text: "hello" }' let z = <const>{ text: "hello" }; 

Esse recurso geralmente significa que tipos que de outra forma seriam usados ​​apenas para sugerir imutabilidade ao compilador podem ser omitidos.


 // Works with no types referenced or declared. // We only needed a single const assertion. function getShapes() { let result = [ { kind: "circle", radius: 100, }, { kind: "square", sideLength: 50, }, ] as const; return result; } for (const shape of getShapes()) { // Narrows perfectly! if (shape.kind === "circle") { console.log("Circle radius", shape.radius); } else { console.log("Square side length", shape.sideLength); } } 

Observe que as anotações acima não eram necessárias. A afirmação const permitiu que o TypeScript usasse o tipo mais específico da expressão.


Advertências


Uma coisa a notar é que as asserções const só podem ser aplicadas imediatamente em expressões literais simples.


 // Error! // A 'const' assertion can only be applied to a string, number, boolean, array, or object literal. let a = (Math.random() < 0.5 ? 0 : 1) as const; // Works! let b = Math.random() < 0.5 ? 0 as const : 1 as const; 

Outra coisa a ter em mente é que os contextos const não convertem imediatamente uma expressão para ser totalmente imutável.


 let arr = [1, 2, 3, 4]; let foo = { name: "foo", contents: arr, }; foo.name = "bar"; // error! foo.contents = []; // error! foo.contents.push(5); // ...works! 

Verificação de globalThis para globalThis


Pode ser surpreendentemente difícil acessar ou declarar valores no escopo global, talvez porque estamos escrevendo nosso código em módulos (cujas declarações locais não vazam por padrão) ou porque podemos ter uma variável local que oculta o nome de um valor global. Em ambientes diferentes, existem maneiras diferentes de acessar qual é efetivamente o escopo global - global em Nó, window , self ou frames no navegador ou em determinados locais fora do modo estrito. Nada disso é óbvio, e geralmente deixa os usuários inseguros se estão escrevendo o código correto.


O TypeScript 3.4 apresenta suporte para a verificação global do novo globalThis do ECMAScript - uma variável global que, bem, se refere ao escopo global. Diferente das soluções acima, globalThis fornece uma maneira padrão de acessar o escopo global que pode ser usado em diferentes ambientes.


 // in a global file: let abc = 100; // Refers to 'abc' from above. globalThis.abc = 200; 

globalThis também é capaz de refletir se uma variável global foi ou não declarada como uma const , tratando-a como uma propriedade readonly quando acessada.


 const answer = 42; globalThis.answer = 333333; // error! 

É importante observar que o TypeScript não transforma referências ao globalThis ao compilar para versões mais antigas do ECMAScript. Dessa forma, a menos que você esteja direcionando navegadores sempre verdes (que já oferecem suporte a globalThis ), convém usar um polyfill apropriado .


Converter em parâmetros nomeados


Às vezes, as listas de parâmetros começam a ficar pesadas.


 function updateOptions( hue?: number, saturation?: number, brightness?: number, positionX?: number, positionY?: number positionZ?: number) { // .... } 

No exemplo acima, é muito fácil para um chamador misturar a ordem dos argumentos fornecidos. Um padrão JavaScript comum é usar um "objeto de opções", para que cada opção seja explicitamente nomeada e a ordem nunca importe. Isso emula um recurso que outros idiomas chamaram de "parâmetros nomeados".


 interface Options { hue?: number, saturation?: number, brightness?: number, positionX?: number, positionY?: number positionZ?: number } function updateOptions(options: Options = {}) { // .... } 

A equipe do TypeScript não trabalha apenas em um compilador - também fornecemos a funcionalidade que os editores usam para recursos avançados, como conclusões, definição e refatoração. No TypeScript 3.4, nossa estagiária Gabriela Britto implementou uma nova refatoração para converter funções existentes para usar esse padrão de "parâmetros nomeados".


Uma refatoração sendo aplicada a uma função para fazer com que ela tome parâmetros nomeados.


Embora possamos alterar o nome do recurso em nosso lançamento final 3.4 e acreditamos que possa haver espaço para algumas das ergonomia, gostaríamos que você experimentasse o recurso e nos desse seu feedback.


Quebrando mudanças


Agora, o nível superior está digitado


O tipo de nível superior agora é digitado como typeof globalThis vez de any . Como conseqüência, você pode receber erros por acessar valores desconhecidos sobre this em noImplicitAny .


 // previously okay in noImplicitAny, now an error this.whargarbl = 10; 

Observe que o código compilado no noImplicitThis não apresentará nenhuma alteração aqui.


Argumentos de tipo genérico propagado


Em certos casos, a inferência aprimorada do TypeScript 3.4 pode produzir funções que são genéricas, em vez daquelas que recebem e retornam suas restrições (geralmente {} ).


 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) // In TypeScript 3.4, 'x.value' has the type // // number[] // // but it previously had the type // // {}[] // // So it's now an error to push in a string. x.value.push("hello"); 

Uma anotação de tipo explícita em x pode se livrar do erro.


O que vem a seguir?


O TypeScript 3.4 é a nossa primeira versão que teve um plano de iteração descrevendo nossos planos para esta versão, que deve se alinhar ao nosso roteiro de 6 meses . Você pode ficar de olho em ambos e em nossa página do roteiro dos recursos contínuos para qualquer trabalho futuro.


No momento, estamos ansiosos para ouvir sobre sua experiência com o RC, então experimente agora e deixe-nos saber seus pensamentos!

Source: https://habr.com/ru/post/pt443996/


All Articles