Релиз TypeScript 2

Вышел релиз языка и уже доступна версия 2.0.3

22 сентября разработчики Microsoft объявили о выходе релиза TypeScript 2.0!

Это событие не может остаться без внимания, т.к. TypeScript несет с собой множество новых фич, а так же обновленния существующих.

За подробностями добро пожаловать под кат, а, чтобы сразу опробовать прочитанное, устанавливайте новую версию. Если вы прямо сейчас выполните:

npm i -g typescript

то вы получите наисвежайшую последнюю версию языка 2.0.3!

Null- and undefined-aware types

В TypeScript есть два специальных типа, Null и Undefined, чьими значения являются null и undefined соответственно. Раньше отсутствовала возможность явно дать имена этим типам, но теперь null и undefinedмогут использоваться, как типы, независимо от режима проверки типов.

Ранее проверка типов считала, что null и undefined могут быть присвоены любому типу. Фактически, null и undefined были допустимыми значениями для любого типа и было невозможно намеренно исключить их (и как следствие невозможно обнаружить ошибочное их использование).

Non-nullable Types

https://msdnshared.blob.core.windows.net/media/2016/09/nonnullable-types-fade.mp4

В TS есть два специальных типа, Null и Undefined, значения которых null и undefined соответственно. Раньше отсутствовала возможность явно дать имена этим типам, но теперь null и undefined могут использоваться как типы, независимо от режима проверки.

Раньше компилятор считал, что null и undefined могут быть присвоены любому типу. Фактически, null и undefined были допустимыми значениями для любого типа и было невозможно намеренно отказаться от них и, как следствие, невозможно обнаружить ошибочное их использование.

Флаг

--strictNullChecks

позволяет переключить компилятор в новый режим строгой проверки Null типа.

В режиме strictNullChecks значения null и undefined уже не являются подтипами составных типов и могут быть значениями только самих себя и any.

Поэтому, несмотря на то, что “T” и “T | undefined” считаются синонимами в обычном режиме (т.к. undefined является подтипом для любого T), они становятся разными типами в строгом режиме и только “T | undefined” разрешает undefined значения. Эти же правила истины для пары “T” и “T | null”.

Примеры:

let x: number;
let y: number | undefined;
let z: number | null | undefined;
x = 1;  // Ок
y = 1; // Ок
z = 1; // Ок
x = undefined;  // Error
y = undefined; // Ок
z = undefined; // Ок
x = null;  // Error
y = null; // Error
z = null; // Ок
x = y;  // Error
x = z; // Error
y = x; // Ок
y = z; // Error
z = x; // Ок
z = y; // Ок

Assigned-before-use checking

Проверка присвоения перед использованием. В этом режиме компилятор требует, чтобы каждой ссылке на локальную переменную с типом отличным от undefined предшествовало присвоение.

Пример

let x: number;
let y: number | null;
let z: number | undefined;
x;  // Error
y; // Error
z; // Ок
x = 1;
y = null;
x;  // Ок
y; // Ок

Компилятор проверяет, что переменным явно присвоены значения, выполняя анализ типов на основании механизма Flow Type Checking.

К необязательным аргументам и свойствам автоматически добавляется тип undefined, даже, если он явно не перечислен в объявлении этих типов. Например:

// x: number | undefined
type T1 = (x?: number) => string;
// x: number | undefined
type T2 = (x?: number | undefined) => string;

Эти два объявленных типа идентичны.

Non-null and non-undefined type guards

Ранее доступ к свойству или вызов функции генерировали ошибку на этапе компиляции, если тип объекта или функции включали undefined или null . Теперь TypeScript осуществляет не-null и не-undefined проверки.

declare function f(x: number): string;
let x: number | null | undefined;
if (x) {
f(x); // Ок, у x тип number
}
else {
// Ошибка, у x тип number? (по сути number|null|undefined)
f(x);
}
let a = x != null ? f(x) : "";  // У a тип string
let b = x && f(x);// У b тип string? (по сути string|null|undefined)

Важно помнить, что к логичскому и(&&) применяется принцип короткого цикла вычислений. Если x имеет значение отличное от null и undefined, то будет произведено вычисление выражения f(x), результат которого будет иметь тип string. Если же x типа null или undefined, то b примет значение x. Таким образом получается, что x может быть типа string или “null|undefined”, что можно сократить как “string?”

Благодаря не-null проверкам для сравнения с null или undefined можно использовать операторы сравнения. Пример:

x != null
x === undefined

Dotted names in type guards

Ранее производилась проверка только для локальных переменных и аргументов функций. Теперь так же “type guards” проверка работает и для переменной или аргумента функции при доступе к одному или более их свойствам.

interface Options {
location?: {
x?: number;
y?: number;
};
}
function foo(options?: Options) {
if (options && options.location && options.location.x)
const x = options.location.x;
}

Компилятор знает, что у x тип number.

Этот механизм так же работает с пользовательскими assert функциями, а так же с операторами typeof и instanceof и не зависят от флага компилятора strictNullChecks.

Типы null и undefined не могут быть расширены типом any в режиме строгой проверки. Допустим у нас есть такая переменная:

let x = null;

В обычном режиме допустимым типом x является any, но в строгом режиме подразумеваемым типом x является null и, как следствие, null является единственным возможным значением в данном примере.

Not null operator

Добавлен новый оператор “!”, который следует после выражения. Оператор подсказывает компилятору, что тип операнда не-null и не-undefined, когда компилятор сам не может определить тип. К примеру, результатом операции x! будет значение типа с исключенными null и undefined. Так же, как и в случае приведения типов через синтаксис “<T>x” и “x as T”, оператор не-null утверждения просто удаляется из скомпилированного JavaScript кода.

function validateEntity(e: Entity?) {
// Бросить исключение, если у e значение null или недопустимый Entity
}
function processEntity(e: Entity?) {
validateEntity(e);
let s = e!.name;
}

Утверждаем, что на данном этапе у e не-null значение и есть доступ к свойству name иначе будет брошено исключение.

Новые возможности могут быть использованы в обычном и строгом режимах проверки типов. В частности, типы null и undefined автоматически вырезаются из объединения типов в обычном режиме проверки типов, а оператор не-null утверждения разрешен, но не оказывает никакого влияния на принятия решения компилятором.

Таким образом обновленные для использования в строгом режиме файлы объявлений (declaration files) все еще могут быть использованы в обычном режиме проверки типов для обратной совместимости.

More Literal Types

Строковые типы литералов появились в версии 1.8. Разработчики хотели, чтобы типы отличные от string так же получили эту возможность. Поэтому в новой версии каждый уникальный тип boolean, number и enum получит свой тип. Используя составные типы можно выражать некоторые сущности более натурально.

type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
let nums: Digit[] = [2, 4, 8];
// Ошибка! 32 не входит в тип 'Digit'!
nums.push(32);

Never type

В TS 2.0 вводится новый примитивный тип never. Этот тип представляет тип значений, которые никогда не произойдут. В частности тип never представляет функции, чей возвращаемый тип данных не определен, а так же переменные, для которых type guards никогда не выполнятся.

Тип never имеет следующие характеристики:

  • Отсутствие типа является подтипом для never, а так же может быть присвоено к never (за исключением самого never).
  • Это подтип, который может быть присвоен любому типу.
  • Если для функционального выражения или стрелочной функции явно не указан тип возвращаемого значения и отсутствуют выражения return или все return возвращают тип never и если точка выхода из функции не доступна (так определил контроль усправления потока), то прогнозируемый тип возвращаемого значения функции будет never.
  • В функциональном выражении, для которого явно указан never в качестве типа возвращаемого значения все return-ы (если они есть) должны возвращать выражения типа never, а так же точка выхода из функции не должна быть достижима.

Т.к. never является подтипом любого типа, он всегд опущен в составных типах и всегда игнорируется в объявлении типа возвращаемого значения функции, если для этой функции указаны другие типы возвращаемого значения.

function error(message: string): never {
throw new Error(message);
}
function fail() { // :never
return error("Something failed");
}
function infiniteLoop(): never {
while (true) {
}
}

Read-only properties and index signatures

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

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

Кроме того, в некоторых ситуациях readonly подразумевается контекстом:

  • Для свойства определен только метод доступа get, но не set.
  • Свойства объекта типа enum считаются readonly свойствами.
  • Свойства экспортируемых из модуля объектов, объявленных через constсчитаются readonly свойствами.
  • Импортируемые (через import) из модулей сущности считаются readonly.
  • Обращение к сущностям импортируемым из модулей, спроектированных в стиле ES2015 считаются readonly (например foo.x доступен только для чтения, если foo объявлен как import * as foo from “foo”).

Примеры:

interface Point {
readonly x: number;
readonly y: number;
}
var p1: Point = { x: 10, y: 20 };
p1.x = 5; // Error!
class Foo {
readonly a = 1;
readonly b: string;
constructor() {
this.b = "hello"; // Ok
}
}

Control flow based type analysis

Ранее анализ типа произведенный на основании механизма type guardings был ограничен условными выражениями if и ?: и не брал во внимание последствия присвоения значений, а так же такие контролирующие поток конструкции, как return и break.

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

Кроме того в режиме строгой проверки типов анализ типа на основании контроля потока так же предупреждает о необходимости явного определения значения у переменных, чей тип не разрешает undefined значений.

Specifying the type of this for functions

Теперь в функциях и методах можно указывать, какой тип this они ожидают.

По умолчанию тип this в функции — any. Начиная с версии 2.0, вы можете явно указать this в качестве аргумента, где this является ложным аргументом, который идет первым в списке аргументов функции:

function foo(this: void) {
// Использование this в теле функции приведет к ошибке
}

Добавлен новый флаг noImplicitThis, который позволяет сообщать об использовании функций, где тип this не указан.

Необязательные свойства и методы класса

Необязательные свойства и методы теперь можно определить внутри класса таким же образом, как это делается в интерфейсах. Пример:

class Foo{
a: number;
b?: number;
  c() { return 1 }
d?() { return 2 }
f?(): number; // Тело может быть опущено
}

Private and Protected Constructors

Конструктор класса может теперь можно объявить как private или protected.

Класс с private конструктором не может быть инициализирован за пределами класса, а так же не может быть расширен.

Класс с protected конструктором тоже не может быть инициализирован, однако может быть расширен.

class Singleton {
private static instance: Singleton;
private constructor() { }
    static getInstance() {
if (!Singleton.instance)
Singleton.instance = new Singleton;
return Singleton.instance;
}
}
let Obj1 = new Singleton(); // Error!
let Obj2 = Singleton.getInstance(); // Ok

Abstract properties and accessors

Внутри абстрактного класса могут быть объявлены абстрактные свойства и/или методы доступа. Любой наследуемый класс обязан реализовать абстрактные свойства или тоже должен быть абстрактным. Абстрактные свойства не могут быть инициализированы. Абстрактные методы доступа не могут иметь тело. Пример:

abstract class Base {
abstract name: string;
abstract get value();
abstract set value(v: number);
}
class Derived extends Base {
name = "derived";
value = 1;
}

Implicit index signatures

Литерал объекта теперь может быть присвоен типу с сигнатурой индекса в том случае, если все известные свойства этого объекта сопоставимы с сигнатурой индекса. Это позволяет передать переменную в качестве аргумента функции, которая ожидает словарь или массив:

function httpService(path: string, headers: { [x: string]: string }) { }

const headers = {
"Content-Type": "application/x-www-form-urlencoded"
};

httpService("", { "Content-Type": "application/x-www-form-urlencoded" }); // Ок
httpService("", headers);  // Раньше это вызвало бы ошибку компилятора, но сейчас - нет

Allow duplicate identifiers across declarations

Разрешены дублирующие объявления между файлами деклараций. Подобные ситуации были основным источником появления ошибок при работе с TS: несколько файлов деклараций объявляли одни и те же cвойства в интерфейсах.

Это ограничение ослабили и теперь компилятор позволяет иметь дублирующие определения между файлами, если они имеют одинаковый тип. Но внутри одного файла дублирования по-прежнему запрещены.

Пример:

// file1.d.ts
interface Foo { a?: string; }
// file2.d.ts
interface Foo {
b?: string;
c?: string;
a?: string; // Ок
}

Trailing commas in function parameter and argument lists

Завершающая запятая в аргументах и параметрах функции теперь разрешена. Эта возможность является реализацией предложения Stage-4 (утверждено и будет реализовано в будущей версии ES) ECMAScript, которое совместимо с предыдущими версиями: ES3/ES5/ES6.

Файлы деклараций внешних модулей

Файлы деклараций это основной способ использования API внешних библиотек в TypeScript, но их получение и расположение не идеально.

Теперь файлы внешних деклараций можно подключать не выходя из npm. В качестве примера, чтобы установить файлы деклараций для библиотеки lodash достаточно всего лишь выполнить npm команду:

npm install --save @types/lodash

Прочие нововведения

Including built-in type declarations with — lib

Подключение встроенных деклараций опцией — lib

Flag unused declarations with — noUnusedParameters and — noUnusedLocals

Появились опции — noUnusedParameters и — noUnusedLocals для уведомления о неиспользуемых объявлениях.

Module identifiers allow for .js extension

Поиск модулей с расширением .js

Support “target: es5” with “module: es6”

Ссылки по теме