为什么要用TypeScript?

正常来讲,只会ESM写法也是足够开发Node.js用的,掌握了TypeScript可以更好的应对大规模应用开发,多花一点时间,对可维护性更好。

  • 良好的类型支持是现代框架必须的功能
  • 可维护性大大提升,为迭代打下坚实基础
  • 适用于大规模开发

Untitled

黑粉

Untitled

Ruby on Rails作者DHH在2023年宣布在Turbo 8中放弃使用TypeScript,笔者以为Rails本身追求的是开发效率,十五分钟写一个Blog,结果加了TypeScript,五分钟还没写完类型定义。敏捷社区喜欢测试驱动开发(TDD),本来只需要改改代码就行,现在还要管类型定义和类型 测试,很显然,这样做的效率和质量是矛盾的,在收益不够高的时候,放弃也是正常。类型和测试都是一个上下文的上文,也就是一个上下文的预设,如果预设类型错误了,全盘皆输,无类型的Javascript则在探索不确定问题上效率更高。

1、Rails是崇尚效率的框架,截图中5分钟那个有误,早在2005年dhh成名之作,就是15分钟写一个Blog。所以加了ts,会阻碍效率,对于这样一个追求极致效率的团队,弃用是正常的。

2、TS适合的是大型项目,团队水平要么很高,要么很低,其实效果都是非常好的。而Rails其实不是很大型的项目,我没太见过,大家都是想快速交付。

3、Ruby社区有敏捷基因,喜欢TDD或XP,代码变动极快的情况下,要把测试改完,要关注类型测试,就会多出非常多的工作。

4、Rails程序员也都是以Ruby为主,js为辅。当js能完成,非要用ts搞,会带来很多负担,对他们而言roi不高。前端之所以喜欢ts,前端会js为主,以ts为辅就很简单。

某些场景确实不需要TS,纯ES已经比js好很多了,这种纯用ES/JS的比例是比较少的。

奉若神教

主要人群:大厂前端、开源贡献者。

TS适合的是大型项目,团队水平要么很高,要么很低。

Untitled

高p会想,下面的一线同学少犯点低级错误,参考现在这故障分,搞得人心惶惶,如果只是麻烦一下一线开发,对稳定性有好处,问题不大。一线同学,大家都觉得ts挺先进,别管会不会,用上再说,不行还有any大法。通常大厂都是会比较愿意用ts的。

  • 规范,比黑魔法要重要,这其实也是选React的原因。
  • 低级错误少,人水平如果高,其实也无所谓的,就怕参差不齐。
  • 不差那点时间,dddd。
  • 不出故障啥都好,稳定性压到一切。
  • 传承会好的多,喜欢研究带着飞。

中立

Untitled

不吹不黑。

我的观点:对于TS,你一定要学,但在不在项目使用依照团队和项目类型而定。

举例看一下ts成本

以tomcat为例。

import debug from 'debug';
import Koa from 'koa';

import { Plugable } from './plugin';
import { mergeDeep } from './utils';

const log = debug('@tomrpc/core/fn');

const ProxyDefaultConfig = {
  proxy: {
    inject: 'before', //init | load | before | after
    before: [],
  },
};

export interface IProxyConfig {
  name?: string | 'tomapp';
  proxy?: {
    inject: 'init' | 'load' | 'before' | 'after';
    before: [];
  };
}

export class Proxy extends Plugable {
  public inject;

  constructor(cfg?: IProxyConfig) {
    super(mergeDeep(ProxyDefaultConfig, cfg));
  }

  proxy() {
    return async (ctx: Koa.BaseContext, next) => {
      log('proxy default');
      await next();
      log('proxy default end');
    };
  }
}

最初的版本,我基本上只加了必要的类型。比如配置项这种。

以hono为例。

import { HonoBase } from './hono-base'
import type { HonoOptions } from './hono-base'
import { RegExpRouter } from './router/reg-exp-router'
import { SmartRouter } from './router/smart-router'
import { TrieRouter } from './router/trie-router'
import type { Env, Schema } from './types'

export class Hono<
  E extends Env = Env,
  S extends Schema = {},
  BasePath extends string = '/'
> extends HonoBase<E, S, BasePath> {
  constructor(options: HonoOptions<E> = {}) {
    super(options)
    this.router =
      options.router ??
      new SmartRouter({
        routers: [new RegExpRouter(), new TrieRouter()],
      })
  }
}

export type MiddlewareHandler<
  E extends Env = any,
  P extends string = string,
  I extends Input = {}
> = (c: Context<E, P, I>, next: Next) => Promise<Response | void>

另外,router也是个好例子

/* eslint-disable @typescript-eslint/ban-ts-comment */
import type { Router, Result } from '../../router'
import { UnsupportedPathError, MESSAGE_MATCHER_IS_ALREADY_BUILT } from '../../router'

export class SmartRouter<T> implements Router<T> {
  name: string = 'SmartRouter'
  routers: Router<T>[] = []
  routes?: [string, string, T][] = []

  constructor(init: Pick<SmartRouter<T>, 'routers'>) {
    Object.assign(this, init)
  }

  add(method: string, path: string, handler: T) {
    if (!this.routes) {
      throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT)
    }

    this.routes.push([method, path, handler])
  }

  match(method: string, path: string): Result<T> {
    if (!this.routes) {
      throw new Error('Fatal error')
    }

    const { routers, routes } = this
    const len = routers.length
    let i = 0
    let res
    for (; i < len; i++) {
      const router = routers[i]
      try {
        routes.forEach((args) => {
          router.add(...args)
        })
        res = router.match(method, path)
      } catch (e) {
        if (e instanceof UnsupportedPathError) {
          continue
        }
        throw e
      }

      this.match = router.match.bind(router)
      this.routers = [router]
      this.routes = undefined
      break
    }

    if (i === len) {
      // not found
      throw new Error('Fatal error')
    }

    // e.g. "SmartRouter + RegExpRouter"
    this.name = `SmartRouter + ${this.activeRouter.name}`

    return res as Result<T>
  }

  get activeRouter() {
    if (this.routes || this.routers.length !== 1) {
      throw new Error('No active router has been determined yet.')
    }

    return this.routers[0]
  }
}

简单,规矩。

trpc