乐趣区

关于typescript:为什么要用TypeScript代码详解ts给我们带来的收益

Typescript 通过这么多年的倒退,曾经战胜了所有的竞争对手,成为了事实上的类型 Javascript 规范,被大量公司所采纳,但还是有人问我为什么要用 ts?应用 ts 能给咱们带来什么益处?这次咱们用一些理论例子来展现应用 ts 给咱们带来的收益。

1. 提供很好的的拼写检查和智能提醒

拼写错误大部分人都会犯的常见谬误。
然而在 js 这种动静语言中、排查拼写错误会很苦楚:

const user = {
  name: '尼古拉斯·赵四',
  pet: {name: '狗子'}
}
user.name.spit('·')

我想将 user 的名字切割成 first name 和 last name,
原本须要应用 split 办法,不小心拼成了 spit。
不过这种的还好,在运行中运行引擎会报错,通知我 spit is not a function
上面这种谬误就很难发现了:

const user = {
  name: '尼古拉斯·赵四',
  pet: {name: '狗子'},
  occupcation:'保安'
}

console.log(user.occupation)

user 有个属性 职业
职业这个词原本应该是 occupation,然而咱们的小伙伴不小心拼成了 occupcation, 多了个 c。
人都会犯错的,没什么大问题。
然而其余应用的小伙伴并不知道这个单词被拼错了,依照失常的 occupation去调用, 那就只会返回一个 undefined 了。
而且可能因为咱们做了一些防空值的解决,导致这种谬误更加难以被发现。
即便发现了,咱们还面临着一个问题,批改这个词可能须要很大的老本,稍后咱们会解说 rename 老本。

ts 能够解决上述的这些问题吗?咱们来试一试

type User = {
  name: string,
  pet: {name: string},
  occupcation:string,
}
const user:User = {
  name: '尼古拉斯·赵四',
  pet: {name: '狗子'},
  occupcation:'保安'
}
user.name.spit('·')//  属性“spit”在类型“string”上不存在。你是否指的是“split”?

console.log('他的工作是',user.occupcation)// 属性“occupcation”在类型“IUser”上不存在。你是否指的是“occupation”?

当你应用 ts 的编译性能,或者的装置了 ts 相干的 lint,很容易就能够失去下面那样的提醒。帮忙咱们在开发阶段就排查出相应的谬误。

同时,咱们还能够用 ts 给变量或参数限定类型,用来躲避 js 默认的隐式转换带来的一些困扰。
可能大家见过一些相似上面这种 js 的面试题:

"" == 0;
[] == 0;
[] == ![];
[] == ![1,2,3];

下面的几个比拟表达式的后果全部都是 true。
js 有一个比较复杂的隐式转换规则,具体能够参考这篇总结 详解一下 javascript 中的比拟

// js 中,0、-0、null、false、NaN、undefined、或者空字符串 "",在比拟运算中都被认为是 false"" == 0;//true
// 比拟的一端是 number,则另一端执行 toPrimitive(value)
// array 的 toPrimitive 返回它的 toString 
// []的 toString()是一个空字符串
// 比拟的一端是 string,另一端是 number, 则将 string 转化为 Number 类型
// 空字符串会被转成 0 Number('') == 0
[] == 0;//true
// 首先执行![],下面讲了判断为 false 的状况,[]作为一个 array 对象为 true,取反后为 false,当比拟两端有一端为 boolean 时,将 bool 值转为 number,false 是 0,true 是 1,这时又变回了上一个问题,反复上一个问题的操作
[] == ![];//true
[] == ![1,2,3];//true
[0]==![0]//true

倡议大家在生产环境中尽量不要应用这些简单的转换个性,避免写错或记错造成 bug。
比拟判断尽量写的含意清晰一些,比方 [].length==0 或者 ![].length 这样,简洁易懂。
或者在 书写中应用三等号 [].length===0 , 三等号会首先验证等号两侧的类型,类型不同会间接返回 false。

隐式转换在代码中也会造成一些隐患:

function incTen(n) {if (n > 1) {return n + 10}
  return n
}

当给这个办法传入一个负数时,返回加 10,否则返回原值 , 当初传进去一个字符串 ”10″ , 会产生什么后果呢?
答案是会返回 "1010"
因为 js 中没有类型的限度,因而为了避免传参谬误,咱们须要在代码中减少很多对参数类型的判断。

function incTen(n) {if(typeof n !== 'number'){throw Error('type is not number')
  }
  if (n > 1) {return n + 10}
  return n
}

编写类型判断语句会消耗工夫不说,即便减少了查看代码,也并不能在第一工夫发现调用谬误,必须在运行的时候能力发现问题。因而咱们能够 应用 ts 来给变量或参数限定类型,第一工夫提醒参数类型的谬误

function incTen(n: number) {if (n > 1) {return n + 10}
  return n
}

incTen('10')// error:类型“"10"”的参数不能赋给类型“number”的参数。

ts 给咱们提供了十分好用的智能提醒,同时也能够将很大一部分谬误在 编写或者编译阶段 就提醒进去,让咱们加以批改。

留神!当你将变量的类型设置为 any 的时候,会绕过类型查看。所以咱们要审慎应用 any 类型,避免 typescript 变成 "anyscript"
// 例:const num:any = '10'
incTen(num) //'1010'

2. 方便快捷的代码重构

在刚刚的例子中讲到了,在 js 中,即便是一个简略的属性 rename,也有可能须要昂扬的老本。
举个简略的例子

// a.js
export const user = {name: '尼古拉斯·赵四',}

// b.js
import {user} from './a'

function callName(user) {console.log(user.name)
}

callName(user)

// c.js
import {user} from './a'

function callName(user) {console.log(user.name)
}

callName(user)

咱们在 a 文件中获取了一个从接口返回的 user,user 中有一个属性叫 name,在 b,c 文件中别离 import 了这个 user 并且输入它的 name

这时需要有了变动,接口返回的 user 中没有了 name,变成了 realName 和 nickName

咱们当初怎么进行批改呢?
有人可能会说全局搜寻 name,全副替换成 nickName 就好了。

那么略微等一下,咱们批改一下代码

// a.js
export const user = {
  name: '尼古拉斯·赵四',
  pet: {name: '狗子'}
}

// b.js
import {user} from './a'

function callName(user) {console.log(user.name)
}

function hisPet(user) {console.log('他的宠物叫:', user.pet.name)
}

callName(user)
hisPet(user)

当初 user 中有个 pet,pet 也有 name,
在全局替换的时候会把 pet 的 name 也替换成 nickName。
这下出错了,pet 的 name 变成了 undefined。

所以很显著,在理论开发中常常不能简略的全局替换,只能全局搜寻之后,排查每一个 name 是否是 a 文件里 user 的 name,之后再一个一个的替换。
如果这是一个有几百个文件的大型项目的话,那一个简略的 rename 都须要半天的工夫。

当初咱们看看 ts 怎么帮咱们解决 rename 问题。

// 首先咱们定义一个 User 类型。// a.ts
export type IUser = {
  name: string
  pet: {name: string}
}
// 将 user 设置为 User 类型
export const user:User = {
  name: '尼古拉斯·赵四',
  pet: {name: '狗子'}
}

// b.ts
import {user,IUser} from './a'

function callName(user:IUser) {console.log(user.name)
}

function hisPet(user) {console.log('他的宠物叫:', user.pet.name)
}

callName(user)
hisPet(user)
// c.ts
import {user,IUser} from './a'

function callName(user:IUser) {console.log(user.name)
}

callName(user)

应用 vscode 的重命名性能 F2,输出新名称并回车。rename 完结。

3. 明确定义函数的参数类型和函数重载

axios是目前大家罕用的前端 HTTP 库,它提供了多种申请形式

  const url='http://www.baidu.com'
  // 1
  axios.get(url)
  // 2
  axios.request({
    url,
    method:'get'
  })
}

如果我申请时这样操作,会发送什么申请呢?

  const url='http://www.baidu.com'
  // 1
  axios.get(url,{
    url:'http://www.google.com',
    method:'post'
  })
  // 后果是什么呢?本人去试一试吧
}

当咱们没有依照作者的志愿来传递参数时,办法会产生什么,只能依赖作者对办法的定义认知,是办法形容优先,还是用户传递的参数形容优先。不同的作者在不同的场景下可能都会有不同的定义
然而 在 ts 中咱们能够定义具体的参数类型 来防止这种状况的产生。

type AxiosRequestConfig = {
  url: string;
  method: Method;
  headers?: any;
  params?: any;
  data?: any;
}
function request(config:AxiosRequestConfig){//do request}
type AxiosGetConfig = {
  headers?: any;
  params?: any;
  data?: any;
}
function get(url:string,config:AxiosGetConfig){// do get}
// 也能够借用 ts 高级一些的个性缩小反复定义
function get(url:string,config:Omit<AxiosRequestConfig,'url'|'method'>){// do get}

很多 js 的库都提供像下面这样的多种调用形式,或者在一个办法中提供多种函数重载,除了像下面的例子一样偶然会让人产生纳闷之外,也有可能因为手误而产生谬误的传参。

// 例:
function logPeople() {if (typeof arguments[0] == 'object') {console.log('name', arguments[0].name)
    console.log('age', arguments[0].age)
  }else {console.log('name', arguments[0])
    console.log('age', arguments[1])
  }
}

当初这个例子实现了一个简略的输入 people 个人信息的办法
你能够传入一个 people 对象

logPeople({name:'赵四',age:18}) //print name 赵四 age 18

也能够将集体属性顺次传入 “

logPeople('赵四',18)//print name 赵四 age 18

如果了解错了用法。就会产生问题

logPeople({name:'赵四'},18)//print name {name: '赵四'} age 18
logPeople('赵四',{age:18})//print name 赵四 age {age:18}

这样的问题还算是比拟容易发现的,在运行时就能够发现
然而一些比拟有责任心的作者为了不便调用者,会对参数提供一些默认值。

function logPeople() {if (typeof arguments[0] == 'object') {
    const p = {
      adulted:true,
      ...arguments[0],
    }
    console.log('name', p.name)
    console.log('age', p.age)
    console.log('adulted', p.adulted)
  }
  else {console.log('name', arguments[0])
    console.log('age', arguments[1])
    console.log('adulted', arguments[2].adulted||true)
  }
}

上述办法提供了一个默认值,是否曾经成年,默认为 true。
这就使得办法写错了也能够失常运行, 输入也是失常,只有遇到一些非凡状况或边界值时,才会产生谬误。
如果咱们的测试用例覆盖范围不够全面,会使得这种谬误难以发现,导致在生产环境中产生一些不可预知的谬误。

logPeople({name:'赵四',age:18},true)

在 ts 中,提供了 定义函数重载的办法,能够帮助咱们躲避这种谬误。

function logPeople(people: { name: string, age: number, adulted?: boolean}): void;
function logPeople(name: string, age: number, adulted?: boolean): void;
function logPeople(): void {if (typeof arguments[0] == 'object') {
    const p = {
      adulted: true,
      ...arguments[0],
    }
    console.log('name', p.name)
    console.log('age', p.age)
    console.log('adulted', p.adulted)
  }
  else {console.log(2)
    console.log('name', arguments[0])
    console.log('age', arguments[1])
    console.log('adulted', arguments[2].adulted ?? true)
  }
}

logPeople({
  name: 'zhaosi',
  age: 18,
})

3. ts 与 c#、java 都很像

这个理由看起来比拟扯,但其实这是一个极大的劣势。
ts 的领导人是一位顶级大佬,安德斯海尔斯伯格,微软的 c#就是这位大佬领导设计的,而 c#的语法又是从 java 而来的,因而 ts 与 c#、java 用起来都很像,这就不便了 java 和 c# 程序员应用 ts 参加到我的项目的开发中。
例如 c#,,在游戏畛域 unity3D 目前是市面上占有率最高的游戏引擎,而 unity3D 应用 c# 作为次要语言。
随着 web 端 3D 渲染的日益成熟,大量的相干从业人员退出到 web 端 3D 渲染的开发中,应用 ts 能够无效缩小他们重新学习一门新语言的累赘。比方白鹭引擎就应用了 ts 作为默认开发语言。开发者能够像写 c# 一样来应用 ts。简直不须要额定的学习老本。

以上就是这次总结的应用 ts 能带来的收益。

退出移动版