Краткое введение в TypeScript
TypeScript набирает популярность за последние несколько лет: 67% разработчиков, которые используют его, назвали его своим самым любимым языком в опросе разработчиков Stack Overflow 2018.
Но что это? Проще говоря, TypeScript - это необязательно статически типизированный надмножество JavaScript.
Отлично, так что вы все можете просто перестать читать, верно?
Возможно, хотя я предполагаю, что вы оказались на этой странице, потому что хотели знать немного больше, чем это! Итак, без лишних слов, давайте разберемся, что это значит.
Подробнее о языках со статической типизацией
Я не буду тратить на это слишком много времени, поскольку уверен, что многие из вас уже знакомы с набором текста (и я не имею в виду 40 слов в минуту), но для тех из вас, кто этого не делает, это практика классификации типа ваших данных в вашем коде. Такие языки проверяют тип данных во время компиляции (а не во время выполнения). TypeScript транспилирован (форма компиляции), что означает, что он компилируется с одного языка на другой на том же уровне (традиционные компиляторы переводят с языка программирования более высокого уровня на язык программирования более низкого уровня) .
Что вы имеете в виду под расширенным набором?
Надмножество языка будет содержать все функции или исходный язык, а также дополнительные функции! Итак, TypeScript содержит все функции JavaScript с необычным улучшением статической типизации.
Прозрачный? Прохладный! Давайте посмотрим на несколько примеров!
Все мои примеры - это серверная часть в Node (хотя она также подходит для внешнего интерфейса).
Вот чистый JavaScript:
var Teapot = function (height, girth, handle, spout) { | |
this.height = height; | |
this.girth = girth; | |
this.handle = handle; | |
this.spout = spout; | |
} | |
Teapot.prototype.isTeaReady = function (tea) { | |
if (tea.checkTeaIsBrewed()) { | |
return "TIP ME OVER AND POUR ME OUT!!"; | |
} | |
} | |
module.exports = Teapot; |
А вот тот же код, но на TypeScript.
export class Teapot { | |
constructor( | |
private height: Height, | |
private girth: Girth, | |
private handle: Handle, | |
private spout: Spout | |
) {} | |
isTeaReady(tea: Tea): string { | |
if (tea.checkTeaIsBrewed()) { | |
return "TIP ME OVER AND POUR ME OUT!!"; | |
} | |
} | |
} |
Итак, что у нас здесь?
Ну, во-первых, конструируем наш класс Teapot. Это принимает четыре аргумента - высоту, обхват, ручку и носик, которые можно использовать только внутри этого класса. Интересно то, что мы определили, какого типа могут быть эти аргументы - т.е. высота, обхват, ручка и носик соответственно. Если мы попытаемся передать аргумент типа «Lid», например, код просто не будет компилироваться.
То же самое и с функцией isTeaReady. Мы не только сообщаем ему, какие параметры он получит, но и сообщаем, какого они типа (например, типа «Чай»). Затем мы можем указать функции, какой тип она должна выводить в конце - в данном случае строку.
Прежде чем мы сможем запустить код, мы должны его скомпилировать. Самый простой способ сделать это - запустить «tsc» - это скомпилирует все файлы в этом каталоге. Вы также можете указать только один файл для компиляции, как я сделал ниже, или вы можете поместить файл tsconfig.json в свой корень, который указывает, какие файлы нужно компилировать. «Tsc» будет проверять наличие этого файла конфигурации. У tsconfig.json есть некоторые дополнительные навороты, поэтому, например, вы можете настроить его так, чтобы он удалял все комментарии при компиляции или не разрешал использовать тип «любой».
То, что мы видим выше, очень положительно - отсутствие TypeErrors или действительно любого другого типа ошибок показывает, что это сработало! Теперь давайте кратко рассмотрим, к чему это привело:
"use strict"; | |
var Teapot = (function () { | |
function Teapot(height, girth, handle, spout) { | |
this.height = height; | |
this.girth = girth; | |
this.handle = handle; | |
this.spout = spout; | |
} | |
Teapot.prototype.isTeaReady = function (tea) { | |
if (tea.checkTeaIsBrewed()) { | |
return "TIP ME OVER AND POUR ME OUT!!"; | |
} | |
}; | |
return Teapot; | |
}()); | |
exports.Teapot = Teapot; |
Как видите, это всего лишь JavaScript, и вы, вероятно, заметили, что этот файл очень похож на чистую версию JavaScript моего чайника.
Некоторые из вас, возможно, заметили в начале, что я сказал, что это «необязательный» статически типизированный язык. Итак (при условии, что вы укажете noImplicitAny как «false» в вашем tsconfig.json) вы можете написать что-то вроде этого:
export class Teapot { | |
constructor( | |
private height, | |
private girth, | |
private handle, | |
private spout | |
) {} | |
isTeaReady(tea) { | |
if (tea.checkTeaIsBrewed()) { | |
return "TIP ME OVER AND POUR ME OUT!!"; | |
} | |
} | |
} |
Как видите, на нем вообще нет текста!
И если кого-то из вас интересует пример…
Оно происходит от популярного британского детского стишка:
Я маленький чайник, маленький и толстый
Вот моя ручка, вот мой носик
Когда чай будет готов, услышь мой крик
Подними меня и вылей.
Зачем мне его использовать?
Многие из преимуществ такие же, как и для других типизированных языков:
- Есть много случаев, когда транспилятор обнаружит и выдаст ошибку, и лучше, чтобы это произошло здесь, чем во время компиляции, чем во время выполнения, поскольку это снижает вероятность возникновения определенных ошибок в реальном времени. Например, предположим, что вы хотите, чтобы ваша программа работала не только с чаем, но и с кофе. В JavaScript вы можете написать что-то вроде этого:
var Coffee = function(water, granules) { | |
this.water = water; | |
this.granules = granules; | |
} | |
module.exports = { | |
Coffee: Coffee | |
} |
Но что, если кто-то попытается налить вам кофе в чайник?
var teaSet = require('./main/teapot'); | |
var coffee = require('./main/coffee'); | |
const myTeapot = new teaSet.Teapot(20, 20, 'curly', 'non-drip'); | |
const cuppaJoe = new coffee.Coffee('filtered', 'Nescafe'); | |
console.log(myTeapot.isTeaReady(cuppaJoe)); |
Есть предположения, что произойдет, если вы запустите приведенный выше код?
Да, верно, получаем TypeError! Во время выполнения! В прямом эфире! Очевидно, что в нашем объекте coffee нет метода checkTeaIsBrewed, поэтому при попытке его запуска мы получаем ошибку. Конечно, вы всегда можете вручную проверить тип, а затем написать тесты, которые запускаются перед отправкой кода, который нарушит живую среду, поэтому мы могли бы написать что-то вроде этого:
Teapot.prototype.isTeaReady = function (tea) { | |
if (tea instanceof Tea) { | |
if (tea.checkTeaIsBrewed()) { | |
return "TIP ME OVER AND POUR ME OUT!!"; | |
} | |
} else { | |
throw('You can only put tea in a teapot!!!'); | |
} | |
} |
И протестируем это так:
var chai = require('chai'); | |
var teaSet = require('../../main/teapot'); | |
var coffee = require('../../main/coffee'); | |
var expect = chai.expect; | |
describe ('Teapot', () => { | |
const myTeapot = new teaSet.Teapot(20, 20, 'curly', 'non-drip'); | |
const tea = new teaSet.Tea(); | |
const cuppaJoe = new coffee.Coffee('filtered', 'Nescafe'); | |
it ('will not make tea if coffee is given', () => { | |
expect(() => { | |
myTeapot.isTeaReady(cuppaJoe) | |
}).to.throw('You can only put tea in a teapot!!!'); | |
}); | |
it('shouts when tea is ready', () => { | |
tea.makeTea(); | |
expect(myTeapot.isTeaReady(tea)).to.equal("TIP ME OVER AND POUR ME OUT!!"); | |
}); | |
}); |
Это подводит нас ко второму важному преимуществу ...
2. Вам не нужно писать так много модульных тестов. Как для человека, который очень сосредоточен на TDD, внезапное открытие того, что существует так много тестов, которые мне больше не нужно писать, было для меня умопомрачительным моментом! В приведенном выше примере мне не нужно было бы вручную проверять, был ли передан в функцию объект типа «Чай», поскольку компилятор проверяет это за меня. Таким образом, все мои модульные тесты действительно должны были бы проверять, что вызов isTeaReady выдаст желаемый ответ.
3. Работа с типизированным языком имеет то преимущество, что когда вы работаете с чужим кодом, он во многих отношениях более понятен и дает вам немного больше информации о том, что делает код, не прибегая к комментариям. Конечно, если ваше именование действительно дурацкое, это все равно может сбивать с толку, TypeScript не заменяет разумного именования! Когда я впервые начал писать на TypeScript, я был совсем новым разработчиком, и мне было действительно полезно видеть, что именно передается и возвращается из функции.
4. «Необязательный» элемент означает, что вы можете легче начать перевод существующей базы кода в TypeScript, так как вы можете обновлять ее по частям. Это имеет свои собственные проблемы, но обеспечивает большую гибкость, чем простое обновление массивной базы устаревшего кода до TypeScript за один раз. Вы можете начать пользоваться функциями раньше!
5. Вы можете использовать функции ES6 и ES7, потому что они будут преобразованы в версию JS, которая будет работать в любом браузере.
6. Есть хорошее сообщество и документация.
Ух ты, правда это продаешь! Где подвох?
- Не все библиотеки поставляются со связанной библиотекой «типов», поэтому вам может потребоваться написать свою собственную. Кроме того, если вы используете Shrinkwrap для сохранения версий используемых вами зависимостей, библиотеки типов считаются зависимостями для разработчиков, поэтому версии не сохраняются. Однако, если в более новой версии будет ошибка, это приведет к поломке кода во время компиляции, а не во время выполнения, поэтому это не повлияет на ваш код в реальном времени.
- Это не займет много времени, но переход от динамически типизированного к статически типизированному может заставить вас задуматься о том, как вы писали код и проектировали его.
- Может показаться немного неуклюжим писать типы для всего, и вы обнаружите, что в конечном итоге импортируете много модулей, просто чтобы вы могли использовать тип (например, если вы хотите передать объект в качестве параметра в функцию в другом модуле). Это может быть особенно неприятно при тестировании.
- Помните, что TypeScript просто компилируется в JavaScript во время выполнения - так что всяких наворотов здесь больше не будет.
Хорошо, я попробую! Как мне его получить?
- Скачать узел и npm
- Запустите ‹npm install -g typescript›
- Расширения файлов: ‹.ts›
И несколько полезных ресурсов: