release

3.4

##Опции компилятора --tsBuildInfoFile

Для того, что бы задать место сохранения файла .tsbuildinfo, в компилятор TypeScript был добавлен флаг --tsBuildInfoFile.

--tsBuildInfoFile - флаг, с помощью которого указывается место сохранения файла .tsbuildinfo генерирующегося при активной опции --incremental и служащий для хранения метаинформации призванной ускорить последующие сборки.

ts
{ "compilerOptions": { "incremental": true, "tsBuildInfoFile": "./buildinfo", } } ```

##Опции компилятора --incremental

В TypeScript, начиная с версии v3.3, при совместном использовании флагов --build и --watch, появилась возможность ускоренной сборки проекта на 50%-75%. Такое ускорение достигается за счет создания при первой сборки метаинформации об изменении файлов хранящейся в памяти. Как можно догадаться, при перезапуске компилятора, метаинформация теряется, а это в свою очередь означает, что процесс ускорения сборки действует только в режиме --watch и не может быть полезно при production сборках.

Для того, что бы достичь ускорения исключительно всех последующих сборок, компилятору TypeScript был добавлен функционал активируемый новым флагом --incremental.

--incremental - флаг, при активации которого, после первой компиляции проекта, в директории заданной атрибутом outDir, создается файл .tsbuildinfo, который хранит метаинформацию об изменении файлов, что позволяет производить ускоренные инкрементальные сборки при всех последующих запусках компилятора.

ts
{ "compilerOptions": { "incremental": true, "outDir": "./dest" } } ``` В случае, когда имя выходного задается с помощью флага `--outFile`, имя генерируемого файла `.tsbuildinfo` будет включать в себя название выходного файла (`.client.tsbuildinfo` для файла `client.js` и `.server.tsbuildinfo` для `server.js` соответственно). `**Примечание:**` создатели _TypeScript_ предупреждают заранее, что генерируемые файлы `.tsbuildinfo` не предназначены для использования сторонними библиотеками, так как их определение, от версии к версии, не будет обладать совместимость. Кроме того, с помощью флага `--tsBuildInfoFile` можно задать место сохранения файла `.tsbuildinfo`. `````ts { "compilerOptions": { "incremental": true, "tsBuildInfoFile": "./buildinfo", "outDir": "./dest" } } ```

##Массивоподобные readonly типы

Начиная с версии v3.4, в TypeScript появилась возможность объявлять Массивоподобные readonly структуры типы, к которым относятся массивы (Array) и кортежи (Tuples). Данный механизм призван защитить элементы массивоподобных структур от изменения и тем самым повысить типобезопасность программ разрабатываемых на TypeScript. Элементы массивоподобных структур объявленных как readonly невозможно заменить или удалить. Кроме того, в подобные структуры невозможно добавить новые элементы.

Для того, что бы объявить readonly массив или кортеж достаточно указать в сигнатуре типа модификатор readonly.

ts
let array: readonly string[] = ['Kent', 'Clark']; // Массив let tuple: readonly [string, string] = ['Kent', 'Clark']; // Кортеж ``` В случаи, объявления `readonly` массива, становится невозможно изменить его элементы с помощью индексной сигнатуры (`array[...]`) `````ts let array: readonly string[] = ['Kent', 'Clark']; array[0] = 'Wayne'; // Error, Index signature in type 'readonly number[]' only permits reading.ts(2542) array[array.length] = 'Batman'; // Error, Index signature in type 'readonly number[]' only permits reading.ts(2542) ``` Помимо этого, у `readonly` массива отсутствуют методы с помощью которым можно изменить элементы массива. `````ts let array: readonly string[] = ['Kent', 'Clark']; array.push('Batman'); // Error, Property 'push' does not exist on type 'readonly number[]'.ts(2339) array.shift(); // Error, Property 'shift' does not exist on type 'readonly number[]'.ts(2339) array.indexOf('Kent'); // Ok array.map((item) => item); // Ok ``` С учетом погрешности на известные различия между массивом и кортежем, справедливо утверждать, что правила для `readonly` массива, справедливы и для `readonly` кортежа. Помимо того, что невозможно изменить или удалить слоты кортежа, он также теряет признаки массива, которые способны привести его к изменению. `````ts let tuple: readonly [string, string] = ['Kent', 'Clark']; tuple[0] = 'Wayne'; // Error, Cannot assign to '0' because it is a read-only property.ts(2540) tuple.push('Batman'); // Error, Property 'push' does not exist on type 'readonly [string, string]'.ts(2339) tuple.shift(); // Error, Property 'shift' does not exist on type 'readonly [string, string]'.ts(2339) tuple.indexOf('Kent'); // Ok tuple.map((item) => item); // Ok ``` Кроме того, механизм массивоподобных `readonly` структур, начиная с версии _TypeScript_ `v3.4`, повлиял на поведение такого расширенного типа, как `Readonly<T>`. До версии `v3.4` поведение типа `Readonly<T>` полноценно распространялось только на объекты. `````ts // Ok, { readonly a: string, readonly b: number } type A = Readonly<{ a: string; b: number }>; // Bad, number[] type B = Readonly<number[]>; // Bad, [string, boolean] type C = Readonly<[string, boolean]>; ``` Но начиная с версии `v3.4` поведение для типа `Readonly<T>` дополняется поведением массивоподобных `readonly` структур. `````ts // Ok, { readonly a: string, readonly b: number } type A = Readonly<{ a: string; b: number }>; // Ok, readonly number[] type B = Readonly<number[]>; // Ok, readonly [string, boolean] type C = Readonly<[string, boolean]>; ``` Напоследок стоит упомянуть, что используя механизм массивоподобных `readonly` структур, по своей сути, компилятор расценивает эти структуры, как принадлежащие к интерфейсу добавленному в версии `v3.4` `ReadonlyArray<T>`,речь о котором пойдет далее.

##Расширенный тип ReadonlyArray

Расширенный тип ReadonlyArray<T> предназначен для создания неизменяемых массивов. ReadonlyArray<T> запрещает изменять значения массива использую индексную сигнатуру array[...].

ts
let array: ReadonlyArray<number> = [0, 1, 2]; array[0] = 1; // Error, Index signature in type 'readonly number[]' only permits reading.ts(2542) array[array.length] = 3; // Error, Index signature in type 'readonly number[]' only permits reading.ts(2542) ``` Кроме того, тип `ReadonlyArray<T>` не содержит методы, способные изменить, удалить существующие или добавить новые элементы. `````ts let array: ReadonlyArray<number> = [0, 1, 2]; array.push(3); // Error, Property 'push' does not exist on type 'readonly number[]'.ts(2339) array.shift(); // Error, Property 'shift' does not exist on type 'readonly number[]'.ts(2339) array.indexOf(0); // Ok ```

##Привидение к константе (const assertion)

Ни для кого не секрет, что с точки зрения JavaScript, а следовательно и TypeScript, все примитивные литеральные значения являются константными значениями. С точки зрения среды исполнения два эквивалентных литерала любого литерального типа являются единым значением. То есть, среда исполнения расценивает два строковых литерала 'text' и 'text', как один литерал. То же самое справедливо и для остальных литералов к которым помимо типа string также относятся такие типы, как number, boolean и symbol.

Тем не менее, сложно найти разработчика TypeScript, который при объявлении каких-либо конструкций, которым предстоит проверка на принадлежность к литеральному типу, не испытывал дискомфорта из-за вывода типов.

ts
type Status = 200 | 404; type Request = { status: Status }; let status = 200; let request: Request = { status }; // Error, Type 'number' is not assignable to type 'Status'.ts(2322) ``` В коде выше ошибка возникает по причине того, что вывод типов определяет принадлежность значения переменной `status` к типу `number`, а не литеральному числовому типу `200`. `````ts // вывод типов видит как let status: number = 200; // в, то время как требуется так let status: 200 = 200; ``` До версии _TypeScript_ `v3.4` без явного указания типа или явного приведения к нему, существовал только один выход из сложившейся, в коде выше, ситуации. Он заключался в утверждении типа, с помощью оператора `as` либо угловых скобок `<>`, непосредственно самого значения нуждающегося в этом. `````ts type Status = 200 | 404; type Request = { status: Status }; let status = 200; // утверждаем компилятору.. let request: Request = { status: status as 200 }; // …с помощью as оператора let request: Request = { status: <200>status }; // …с помощью угловых скобок // …, что должен рассматривать значение ассоциированное со 'status', как значение принадлежащие к литеральному типу 'Status' ``` _TypeScript_, начиная с версии `v3.4`, вводит такое понятие, как `const assertion` (утверждение к константе или константное утверждение). Константное утверждение это такое утверждение объявление которого производится с помощью оператора `as` либо угловых скобок `<>`. `````ts type Status = 200 | 404; type Request = { status: Status }; let status = 200; let request: Request = { status: status as const }; // Ok let request: Request = { status: <const>status }; // Ok ``` По причине того, что компилятор получает инструкции заставляющие его расценивать значение как константное, вывод типов определяет его принадлежность к литеральному типу. Утверждение к константе заставляет вывод типов определять принадлежность массива к типу `readonly tuple`. `````ts let a = [200, 404]; // let a: number[] let b = [200, 404] as const; // let b: readonly [200, 404] let c = <const>[200, 404]; // let c: readonly [200, 404] ``` В случае с объектным типом, утверждение к константе рекурсивно помечает все его поля как `readonly`. Кроме того, все его поля принадлежащие к примитивным типам расцениваются как литеральные типы. `````ts type NotConstResponseType = { status: number; data: { role: string; }; }; type ConstResponseType = { status: 200 | 404; data: { role: 'user' | 'admin'; }; }; let a = { status: 200, data: { role: 'user' } }; // NotConstResponseType let b = { status: 200, data: { role: 'user' } } as const; // ConstResponseType let c = <const>{ status: 200, data: { role: 'user' } }; // ConstResponseType ``` Но стоит помнить, что утверждение к константе, применимо исключительно к литералам `number`, `string`, `boolean`, `array` и `object`. `````ts let a = 'value' as const; // Ok - 'value' является литералом, let a: "value" let b = 100 as const; // Ok - 100 является литералом, let b: 100 let c = true as const; // Ok - true является литералом, let c: true let d = [] as const; // Ok - [] является литералом, let d: readonly [] let e = { f: 100 } as const; // Ok - {} является литералом, let e: {readonly f: 100;} let value = 'value'; let array = [0, 1, 2]; // let array: number[] let object = { f: 100 }; // let object: {f: number;} let f = value as const; // Error - value это ссылка идентификатор хранящий литерал let g = array as const; // Error - array это ссылка на идентификатор хранящий ссылку на объект массива let h = object as const; // Error - object это ссылка идентификатор хранящий ссылку на объект объекта ``` Но, кроме того, все три рассмотренных случая утверждения к константе (примитивных, массивов и объектных типов) может создать впечатление, что в _TypeScript_, наконец, появились структуры, которые справедливо назвать теми самыми, неизменяемыми ни при каких условиях, константами. И это действительно так, но лишь от части. Дело в том, что на момент версии _TypeScript_ `v3.4` принадлежность объектных и массивоподобных типов к константе зависит от значения с которыми они ассоциированы. В случае, когда литералы ссылочных типов (массивы и объекты) ассоциированы при помощи агрегационных отношений со значением также принадлежащим к ссылочному типу, то они представляются такими, какими были на момент ассоциации. Кроме того, поведение механизма приведения к константе зависит от другого механизма – деструктуризации. `````ts let defaultObject = { f: 100 }; // let defaultObject: {f: number;} let constObject = { f: 100 } as const; // let constObject: {readonly f: 100;} let defaultArray = [0, 1, 2]; // let defaultArray: number[] let constArray = [0, 1, 2] as const; // let constArray: readonly [0, 1, 2] // неожиданно - o0.f не имеет модификатора readonly! Однако ожидаемо, что o0.f.f иммутабельный (неизменяемый) объект let o0 = { f: { f: 100 } } as const; // {f: {readonly f: 100;};} // ожидаемо - o1.f имеет модификатор readonly. Вполне ожидаемо: o1.f.f мутабельный (изменяемый) объект let o1 = { f: defaultObject } as const; // {readonly f: {f: number;};} // ожидаемо - o2 иммутабельный (неизменяемый) объект let o2 = { ...defaultObject } as const; // {readonly f: number;} // неожиданно - o3.f не имеет модификатора readonly. ожидаемо- o3.f.f иммутабельный (неизменяемый) объект let o3 = { f: { ...defaultObject } } as const; // {f: {readonly f: number;};} // ожидаемо - o4.f и o4.f.f иммутабельные (неизменяемые) объекты let o4 = { f: constObject } as const; // let o4: {readonly f: {readonly f: 100;};} // ожидаемо - o5 иммутабельный (неизменяемый) объект let o5 = { ...constObject } as const; // let o5: {readonly f: 100;} // неожиданно - o6.f не имеет модификатора readonly. ожидаемо- o6.f.f иммутабельный (неизменяемый) объект let o6 = { f: { ...constObject } } as const; // {f: {readonly f: 100;};} ``` По причине того, что не примитивные (объектные) типы данных, хранящиеся в массиве, подчиняются правилам описанным выше, подробное рассмотрение процесса утверждения массива к константе будет опущено. И последнее о чем стоит упомянуть, утверждение к константе применимо только к простым выражениям. let a = (Math.round(Math.random() _ 1) ? 'yes' : 'no') as const; // Error let b = Math.round(Math.random() _ 1) ? 'yes' as const : 'no' as const; // Ok, let b: "yes" | "no"

##Проверка типов для globalThis

Поскольку реализация глобальной области зависит от конкретной платформы, доступ к ней, соответственно, отличается. В браузере, доступ к глобальному объекту осуществляется при помощи ссылки window, в, то время как в nodejs туже роль исполняет ссылка global.

Для упрощения работы с глобальным объектом, в ECMAScript10 было предложено стандартизировать имя идентификатора ссылающего на него. Так в ES2019 появилось новое глобальное свойство globalThis, которое предоставляет доступ к глобальному объекту независимо от платформы.

Начиная с версии v3.4 в TypeScript также было представлено новое свойство globalThis, и, кроме того, компилятор tsc получил поддержку проверки типов для полей объявленных в глобальном модуле. Помимо того, что объявленные в глобальном контексте члены, получили поддержку вывода типов, благодаря этому механизму, современные ide стали предоставлять разработчикам механизм умного автокомплита из нового глобального модуля.

ts
// где-то в глобальном файле let globalValue = 100; const GLOBAL_VALUE = '100'; // где-то в приложении let a = globalValue; // let a: number let b = GLOBAL_VALUE; // let b: string globalThis.globalValue = 101; // Ok globalThis.GLOBAL_VALUE = '101'; // Error ``` Важным моментом является то, что _TypeScript_ не преобразует свойство `globalThis` при компиляции в более старые версии _ECMAScript_. Помимо этого, введение текущего функционала привело к изменению поведения предыдущих компиляторов. Если до версии `v3.4` допускалось объявлять члены в глобальном объекте с помощью ссылки `this`, то начиная с версии `v3.4`, такой код вызывает ошибку. `````ts // до версии 3.4 this.a = 'value'; // Ok globalThis.b = 'value'; // Error // начиная с версии 3.4 this.a = 'value'; // Error globalThis.b = 'value'; // Ok ```