make javascript java again

TypeScript стал одним из необходимых навыков для современного веб разработчика. В 2019 он вошел в ТОП-10 наиболее используемых языков на GitHub, его поддержку полностью добавили в Create react app, и можно найти массу других свидетельств роста его популярности. В то же время такие языки, как Java и C продолжают сдавать позиции.

Когда мы говорим о преимуществах TypeScript, на ум обычно приходит следующий список:

  • TypeScript поддерживает статическую типизацию
  • TypeScript делает код проще для чтения и понимания
  • TypeScript помогает избежать множества болезненных багов, которые обычно совершают разработчики, благодаря проверке типов в коде
  • TypeScript поощряет разработчиков следовать лучшим ООП практикам
  • Как следствие вышеперечисленного — TypeScript экономит время разработчиков

Интересно, что все пункты этого списка принимаются на веру без критического взгляда. Сегодня я предлагаю Вам рассмотреть эти пункты внимательней и выяснить, действительно ли они настолько полезны для нас.

Поскольку главной задачей TypeScript является уменьшение количества багов, давайте определимся, почему это так важно? Ответ прост: чем меньше багов — тем меньше дорогостоящего времени разработчиков и тестировщиков мы потратим, следовательно — получим стоящий продукт за меньшие деньги и он начнет приносить доход раньше.

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

Статическая типизация — волшебная таблетка от багов?


Главная фича TypeScript — поддержка статической типизации. Среди разработчиков распространено убеждение, будто динамическая типизация — источник чуть ли не всех проблем JavaScript разработчиков.

Я гадаю, что плохого люди находят в динамической типизации? Почему на другие динамические языки, например, Python и Ruby, не обрушивается такой шквал критики? Могу лишь предполагать, что проблема не столько в динамической типизации, сколько в приведении типов в JavaScript. Действительно, иногда может очень неприятно удивить код типа такого:

javascript types conversion

Но это проблема скорее плохого знания JavaScript, а не самого языка. Представьте, что у вас есть мощная спортивная машина. Но ваши водительские навыки достаточно посредственные. Вы будете требовать от производителя машины внести изменения, чтобы уменьшить максимальную скорость машины или научитесь продвинутому вождению и станете выдающимся профессионалом? То, что предлагает нам TypeScript — ограничить возможности динамической типизации вместо того, чтобы как следует выучить JavaScript.

Еще один вопрос к противникам динамической типизации — если статическая типизация так хороша, почему мы все еще встечаем баги в коде, написанном на Java и C#? Да, мы можем отловить эти ошибки на этапе компиляции, но, давайте будем честны, этого недостаточно. Мы должны следовать SOLID принципам и другим лучшим практикам, чтобы обеспечить качественный код. И снова — это относится в большей степени к знаниям и квалификации программистов, чем к языку программирования.

Действительно ли легче читается TypeScript код?


Приведу 2 примера Redux Thunk:

const getUsers = async dispatch => {
  //...
  try {
    const users = await APIService.get('/users')
    dispatch(successGetUsers(users))
  } catch(err) {
    dispatch(failedGetUsers(err))
  }
}

и тоже самое на TypeScript:

const getUsers = (): ThunkAction<void, {}, {}, AnyAction> => 
  async (dispatch: ThunkDispatch<{}, {}, AnyAction>) => {
    //...
    try {
      const users: User[] = await APIService.get('/users')
      dispatch(successGetUsers(users))
    } catch(err) {
      dispatch(failedGetUsers(err))
    }
  }

Что значат все эти дженерики? Зачем в них 2 пустых объекта? Сколько времени мы должны потратить на чтение документации, чтобы во всем этом разобраться? Хорошо, это еще нормально для Redux Thunk, поскольку это очень популярная middleware для Redux, у них отличная команда поддержки и документаци��. Но что, если нам придется поддерживать код, который не имеет всего этого?

Больше не будет трудноуловимых багов?


В предыдущем примере Вы увидели, как TypeScript делает код более многословным. Как известно, чем больше и сложнее система — тем больше вероятность ошибки. Помимо тех ошибок, которые мы могли допустить в JavaScript коде, мы должны еще следить за тем, чтобы не сделать опечатку в интерфейсах, ошибку в дженериках, и т.д. Справедливости ради стоит заметить, что компилятор поможет быстро найти и исправить такие ошибки, но без TypeScript мы бы вообще с ними не столкнулись.

Следование лучшим практикам ООП


Как мы знаем, мы должны следовать лучшим практикам, если хотим писать качественный, легко масштабируемый и поддерживаемый код. И TypeScript может нам в этом помочь. Звучит отлично, давайте рассмотрим пример из Express:

userRoute.js

router.get('/users', (req, res) => {
  //get users from DB
  res.json(users)
})
router.post('/users', (req, res) => {
  //create user 
  res.json(userCreated)
})

userRoute.ts

class UserRouter {
  public router = express.Router() 
  public address = '/users'
  constructor() {
    this.initRoutes()
  }
  initRoutes() {
    this.router.get(this.address, this.getUsers)
    this.router.post(this.addressm this.createUser)
  }
  getUsers(req: express.Request, res: express.Response) {
    //get users from DB
    res.json(users)
  }
  createUser(req: express.Request, res: express.Response) {
    //create user
    res.json(userCreated)
  }
}

Первое, что бросается в глаза — опять пишется больше кода. Мы используем классы, чтобы писать в ООП стиле. Нам придется потом создавать экземпляры этих классов. В добавок, мы можем использовать интерфейсы, статические типы и другие конструкции. И чтобы это все не превратилось в полнейший хаос, нам не остается ничего, кроме как использовать best practices. И это то, что называется: «поощрение разработчиков к использованию лучших ООП практик».

Когда разработчики проголосовали за простые, гибкие и динамичные языки, такие как JavaScript и Python, когда парадигма функционального программирования показала свои преимущества и способность решать некоторые задачи более изящно и эффективно (и в JavaScript мы можем использовать оба подхода), TypeScript подталкивает нас к тому, чтобы писать код по-старинке в стиле ООП Java и C#.

Подводя итог, мы увидели, что TypeScript в действительности не экономит наше время, он ни защищает нас от большого количества багов, ни увеличивает нашу производительность. Более того, он требует написания большего количества кода, дополнительной конфиг��рации, type definition файлов, траты времени на чтение дополнительной документации. Новичок скорее напишет, может, не идеальное, но работающее приложение на JavaScript или Ruby, пока мы будем писать отличное TypeScript приложение в соотвествии со всеми принципами. А потом он наймет нас переписывать его стартап, где мы сможем использовать TypeScript, чтобы сделать все правильно.

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

Я вижу это так:
Окей, мы больше не хотим Java, хотим JavaScript. Но нам не нравится JavaScript таким, как он есть, поэтому давайте сделаем его немножечко более похожим на Java. Отлично, теперь у нас есть все, что было в Java, при этом это не Java. Погнали!