Не делайте этого - Часть вторая

Проблема с Typescript

Фон

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

Это вторая часть статьи, состоящей из двух частей. Переходим к предыдущей части, в которой я делаю большой дамп объектно-ориентированного программного обеспечения, особенно в том, что касается Javascript.

Итак, давайте поговорим о Typescript

Typescript - это строго типизированный (отсюда и название) надмножество Javascript. Это отличный способ для разработчиков, использующих строго типизированные объектно-ориентированные языки, такие как Java или C #, для разработки приложений Javascript с защитным слоем строгой типизации и автозавершения кода IDE. Это также отличный способ для неопытных или некомпетентных разработчиков создать поистине впечатляющую гору технического долга.

TypeScript побуждает разработчиков думать, что они работают на типобезопасном и объектно-ориентированном языке. Но реальность такова, что Typescript компилируется до Javascript и позволяет вам перемежать столь знакомый типобезопасный объектно-ориентированный код с необработанным, чистым Javascript. И в этом вся загвоздка.

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

Машинопись - это костыль

Но это шаткий костыль.

Машинопись поощряет использование объектно-ориентированного программирования. В первой части я обсуждал объектно-ориентированный объект и, в частности, отметил, что объектно-ориентированный объект Javascript - это то, чего следует избегать, за исключением тех редких случаев, когда вам действительно нужны объекты многократного использования.

Машинопись многословна и уродлива

Вот пример. Декомпозиция объектов - жизненно важный инструмент в любом наборе инструментов разработчика Javascript. Но Typescript действительно может все испортить.

Очень распространенная задача - сопоставить имена свойств одного объекта с другим, скажем, потому, что вы извлекли некоторый JSON из API и вам нужно, чтобы имена свойств были разными. Итак, вы можете написать функцию сопоставления, например:

const mapUser = ({
  user_name: username,
  first_name: firstName,
  last_name: lastName
}) => ({
  username,
  firstName,
  lastName
})

Это совершенно корректный и очень подозрительный Javascript, но он наверняка запутает Typescript. Чтобы написать это в Typescript, вам нужно сделать что-то вроде этого:

const mapUser = ({
  user_name: username,
  first_name: firstName,
  last_name: lastName
} : {
  username: string,
  firstName: string,
  lastname: string
}) => ({
  username,
  firstName,
  lastName
} : any);

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

Если вы используете эту функцию для сопоставления полей из файла JSON, ваша навороченная проверка типобезопасности в любом случае выйдет прямо из окна.

const getUsers = async url => {
  const { json } = await fetch(url)
  const data = await json()
  return data.map(mapUser)
}

Как Typescript узнает, что находится в data? Это не так. И предполагаемая безопасность типов Typescript исчезает, как только он компилируется в Javascript.

TypeScript поощряет зависимость от IDE

Может быть, это только я, но я просто терпеть не могу большинство IDE. То, что людям нравится в них, например автозаполнение, сводит меня с ума. Разработка программного обеспечения улучшается не за счет увеличения скорости набора текста, а за счет улучшения ваших рассуждений. Если у вас есть приличная окраска синтаксиса, встроенный файловый браузер и хороший поиск и замена, это действительно все, что нужно любому достойному разработчику Javascript. Я использую Atom с очень минимальным набором плагинов. Я также иногда использую TextMate, потому что по какой-то непостижимой причине Atom не позволяет вам перетаскивать файлы из одного проекта в другой.

Но для правильного использования Typescript вам действительно нужно полностью погрузиться в Microsoft VS Code или возиться с WebStorm или хуже. Люди, пришедшие с Java или C #, могут тосковать по Eclipse или NetBeans, но никто другой не будет.

Typescript вносит свои странные ошибки

В своем сообщении в блоге Хорошее, плохое и уродливое в TypeScript Леонардо Фрейтас объясняет:

Если вы такой же любитель функционального программирования, как я, связывание каррированных методов с обещаниями иногда может быть проблематичным. Это происходит потому, что некоторые фреймворки используют собственные ES Promises, а другие используют сторонние библиотеки обещаний, такие как Bluebird. Несмотря на то, что у вас не возникнет проблем с их объединением в JS, TypeScript видит их как разные типы и откажется даже транспилировать, поэтому выдает TypeError.

Машинопись означает, что вам нужен препроцессор

Front-end разработчики привыкли использовать препроцессоры, такие как Babel, и создавать инструменты, такие как Webpack, чтобы превратить свой суперсовременный Javascript в то, что могут понять браузеры. Но для back-end разработчиков, использующих NodeJS, нет необходимости в инструментах сборки, а препроцессоры просто добавляют раздражающий уровень сложности, который не представляет никакой ценности. Развертывание сервера, который необходимо построить, прежде чем его можно будет использовать, утомительно.

Мокинг в модульных тестах сложнее с Typescript

Фронтенд-разработчики могут использовать тестовые фреймворки, такие как Jest, которые включают поистине потрясающие возможности имитации. Но они запутываются, когда вы добавляете в микс Typescript. А для back-end разработчиков, использующих Node, где чаще используется тестовая среда mocha, наряду с утилитами-имитаторами, такими как proxyquire, вставка любого этапа сборки с предварительной обработкой усложняет настройку теста.

Модульное тестирование важно. Я читал всевозможные статьи, в которых утверждается, что вам не нужно тщательное модульное тестирование при использовании Typescript из-за его предполагаемой безопасности типов во время компиляции, но это просто чушь. Я также читал мнения о том, что если вы издеваетесь, значит, вы неправильно проводите тестирование; это тоже чушь. Верно как раз обратное.

Отступление: В защиту издевательства

Настоящие модульные тесты проверяют только конкретные рассматриваемые модули кода. Если ваш код похож:

const fetch = require('node-fetch)
const mapUser = require('src/utils/mapUser')
const getUsers = async url => {
  const { json } = await fetch(url)
  const data = await json()
  return data.map(mapUser)
}

Вы можете протестировать его так:

const { expect } = require('chai')
const { stub } = require('sinon')
const proxyquire = require('proxyquire')
describe('getUsers', () => {
  const fetch = stub()
  const json = stub()
  const data = ['some data']
  const mapUser = stub()
const response = { json }
  const url = 'some-url'
const getUsers = proxyquire('src/getUsers', {
    'node-fetch': fetch,
    'src/utils/mapUser': mapUser
  })
  const expected = ['some result']
let result
before(async () => {
    fetch.resolves(response)
    json.resolves(data)
    mapUser.returns(expected[0])
    result = await getUsers(url)
  })
  
  it('called fetch with the supplied url', () => {
    expect(fetch).to.have.been.calledWith(url)
  })
  
  it('called json', () => {
    expect(json).to.have.been.calledOnce
  })
  
  it('called mapUser once with the right data', () => {
    expect(mapUser).to.have.been.calledOnceWith(data[0])
  })
  
  it('returned the expected result', () => {
    expect(result).to.deep.equal(expected)
  })
})

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

Модульные тесты, конечно, не заменяют реальных интеграционных тестов, но это совсем другая история.

Машинопись - это Volvo языков программирования

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

На этом завершается вторая часть.

Вернитесь к части первой, чтобы прочитать мою общую критику объектно-ориентированного подхода и особенно объектно-ориентированного подхода, как это сделано с помощью Javascript.

Мы узнали, что:

  • TypeScript поощряет чрезмерное использование объектов
  • TypeScript дает разработчикам ложное чувство безопасности
  • TypeScript делает ваш код уродливым и трудным для тестирования
  • Вы не должны использовать Typescript

Ссылки

Статьи

Технологии

Нравится, но не подписчик? Вы можете поддержать автора, присоединившись через davesag.medium.com.