Выпустив версию Typescript 1.3, мы сфокусировались на усовершенствовании системы типов и добавлении функционала ECMAScript 6 в TypeScript. Давайте рассмотрим некоторые новые возможности, которыми вы сможете пользоваться в новой версии.
Все описанные в статье вещи уже реализованы в мастер-ветке нашего репозитория на Github — вы можете выкачать ее и попробовать их уже сейчас.
Новые возможности позволяют более аккуратно и легко работать с переменными, которые имеют различный тип во время исполнения. Они сокращают количество мест, где нужно явно указывать тип, проверять его или использовать тип any
. Авторы типизирующих файлов (.d.ts
) могут также использовать эти возможности для описания внешних библиотек. Те, кто следят за развитием компилятора, могли заметить, что мы сами тоже ими пользуемся.
Типы-объединения
Общие сведения
Типы-объединения — это мощный способ выразить значение, которое может иметь один из нескольких типов. Допустим, у вас может быть API для выполнения программы, который принимает аргументы командной строки в виде string
или string[]
. Теперь можно написать так:
interface RunOptions {
program: string;
commandline: string[]|string;
}
Присвоение значений такого типа работает интуитивно — любое значение, которое можно было бы присвоить любому из членов перечисления, сработает:
var opts: RunOptions = /* ... */;
opts.commandline = '-hello world'; // OK
opts.commandline = ['-hello', 'world']; // OK
opts.commandline = [42]; // Ошибка: тип number не совместим с string или string[]
При обращении к переменной, можно напрямую использовать свойства, которые являются общими для всех типов в объединении:
if(opts.commandline.length === 0) { // У string и string[] есть свойство length
console.log("Пусто!");
}
С помощью ограничителей типов, работать с переменной типа-объединения легко и просто:
function formatCommandline(c: string[]|string) {
if(typeof c === 'string') {
return c.trim();
} else {
return c.join(' ');
}
}
Ограничители типов
Распространенная практика в Javascript — использовать операторы typeof
или instanceof
для определения типа значения во время исполнения. Typescript теперь понимает эти конструкции и использует их при выводе типа, если они используются в блоке условия:
var x: any = /* ... */;
if(typeof x === 'string') {
console.log(x.subtr(1)); // Ошибка: в типе 'string' нет метода 'subtr'
}
// в этой области видимости переменная 'x' все еще типа 'any'
x.unknown(); // OK
Вот так можно пользоваться instanceof
с классами и объединенными типами:
class Dog { woof() { } }
class Cat { meow() { } }
var pet: Dog|Cat = /* ... */;
if(pet instanceof Dog) {
pet.woof(); // OK
} else {
pet.woof(); // Error
}
Более строгие обобщенные типы
Мы также решили сделать проверку обобщенных вызовов более строгой в некоторых случаях. Например, раньше такой код, как ни странно, компилировался без ошибок:
function equal<T>(lhs: T, rhs: T): boolean {
return lhs === rhs;
}
// Раньше: никаких ошибок
// Новое поведение: Ошибка - нет общего типа между 'number' и 'string'
var e = equal(42, 'hello');
Улучшенный вывод типов
Обобщенные типы позволяют улучшить качество проверок при использовании массивов и в других местах, где возможно использование нескольких разных типов в одной коллекции:
var x = [1, 'world']; // x: Array<string|number>
x[0] = 'hello'; // OK
x[0] = false; // Ошибка - тип 'boolean' не является ни 'number', ни 'string'
Псевдонимы для типов
Можно объявить псевдоним для типа с помощью ключевого слова type
:
type PrimitiveArray = Array<string|number|boolean>;
type MyNumber = number;
type NgScope = ng.IScope;
type Callback = () => void;
Псевдоним типа является его полным синонимом, они полностью взаимозаменяемы при использовании.
В следующей статье я расскажу о возможностях ECMAScript 6, которые мы добавляем в Typescript. Чтобы узнать больше и попробовать самому, выкачайте ветку master
из репозитория Typescript на Github, попробуйте и поделитесь с нами.
Примечание переводчика
В Typescript 1.3 появилась возможность использовать кортежи из массивов. Однако автоматический вывод типов при этом не происходит:
// ожидается: [number, string]
// фактически будет: Array<string|number>
var x = [1, 'world'];
Почему так сделали? Автоматический вывод типов-кортежей ломает множество сценариев использования, например:
var x = [dog, cat, animal]; // тип для 'x' будет 'Animal[]'
x[0] = new Frog();
Если бы тип переменной x
был выведен как кортеж [Dog, Cat, Animal]
, то присвоение во второй строке вызвало бы ошибку. Авторы посчитали более правильным требовать явного указания кортежей, и это звучит довольно логично.
Кроме того, кортежи совместимы с типами-перечислениями в одностороннем порядке:
var x : [number, string] = [1, "test"];
var y : Array<number|string> = x; // Все в порядке
x = y; // ошибка: типы совместимы только в одну сторону
Скорее бы поддержка этих возможностей попала в Resharper!
UPD: Не пора ли создать отдельный хаб под Typescript?
Автор: impwx