release

3.4

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

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

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

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

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

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

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

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

ts
{
    "compilerOptions": {
        "incremental": true,
        "outDir": "./dest"
    }
}
```

В случае, когда имя выходного задается с помощью флага `--outFile`, имя генерируемого файла `.tsbuildinf` будет включать в себя название выходного файла (`.client.tsbuildinf` для файла `client.js` и `.server.tsbuildinf` для `server.js` соответственно).

`**Примечание:**` создатели _TypeScript_ предупреждают зарание, что генерируемые файлы `.tsbuildinf` не предназначены для использования сторонними библиотеками, так как их определение, от версии к версии, не будет обладать совместимость.

Кроме того, с помощью флага `--tsBuildInfoFile` можно задать место сохранения файла `.tsbuildinf`.

`````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]>;
```

,fdktyyj
Но начиная с версии `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 reuest: 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 reuest: Request = { status: status as 200 }; // …с помощью as оператора
let reuest: 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 reuest: Request = { status: status as const }; // Ok
let reuest: 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
```