TypeScript学习笔记【CoderWhy】

TypeScript学习笔记【CoderWhy】

引言

JavaScript一门优秀的语言

  • 我始终相信:任何新技术的出现都是为了解决原有技术的某个痛点。
  • JavaScript是一门优秀的编程语言吗?
    • 每个人可能观点并不完全一致,但是从很多角度来看,JavaScript是一门非常优秀的编程语言;
    • 而且,可以说在很长一段时间内这个语言不会被代替,并且会在更多的领域被大家广泛使用;
  • 著名的Atwood定律:
    • Stack Overflow的创立者之一的 Jeff Atwood 在2007年提出了著名的 Atwood定律。
    • any application that can be written in JavaScript, will eventually be written in JavaScript.
    • 任何可以使用JavaScript来实现的应用都最终都会使用JavaScript实现。
  • 其实我们已经看到了,这句话正在一步步被应验:
    • Web端的开发我们一直都是使用JavaScript;
    • 移动端开发可以借助于ReactNative、Weex、Uniapp等框架实现跨平台开发;
    • 小程序端的开发也是离不开JavaScript;
    • 桌面端应用程序我们可以借助于Electron来开发;
    • 服务器端开发可以借助于Node环境使用JavaScript来开发。

JavaScript的痛点

  • 并且随着近几年前端领域的快速发展,让JavaScript迅速被普及和受广大开发者的喜爱,借助于JavaScript本身的强大,也让使用JavaScript开发的人员越来越多。
  • 优秀的JavaScript没有缺点吗?
    • 其实上由于各种历史因素,JavaScript语言本身存在很多的缺点;
    • 比如ES5以及之前的使用的var关键字关于作用域的问题;
    • 比如最初JavaScript设计的数组类型并不是连续的内存空间;
    • 比如直到今天JavaScript也没有加入类型检测这一机制;
  • JavaScript正在慢慢变好
    • 不可否认的是,JavaScript正在慢慢变得越来越好,无论是从底层设计还是应用层面。
    • ES6、7、8等的推出,每次都会让这门语言更加现代、更加安全、更加方便。
    • 但是知道今天,JavaScript在类型检测上依然是毫无进展(为什么类型检测如此重要,我后面会聊到)。

类型带来的问题

  • 首先你需要知道,编程开发中我们有一个共识:错误出现的越早越好
    • 能在写代码的时候发现错误,就不要在代码编译时再发现(IDE的优势就是在代码编写过程中帮助我们发现错
      误)。
    • 能在代码编译期间发现错误,就不要在代码运行期间再发现(类型检测就可以很好的帮助我们做到这一点)。
    • 能在开发阶段发现错误,就不要在测试期间发现错误,能在测试期间发现错误,就不要在上线后发现错误。
  • 现在我们想探究的就是如何在 代码编译期间 发现代码的错误:
    • JavaScript可以做到吗?不可以,我们来看下面这段经常可能出现的代码问题。

类型错误

  • 这是我们一个非常常见的错误:
    • 这个错误很大的原因就是因为JavaScript没有对我们传入的参数进行任何的限制,只能等到运行期间才发现这个错误
    • 并且当这个错误产生时,会影响后续代码的继续执行,也就是整个项目都因为一个小小的错误而深入崩溃;
  • 当然,你可能会想:我怎么可能犯这样低级的错误呢?
    • 当我们写像我们上面这样的简单的demo时,这样的错误很容易避免,并且当出现错误时,也很容易检查出来;
    • 但是当我们开发一个大型项目时呢?你能保证自己一定不会出现这样的问题吗?而且如果我们是调用别人的类库,又如何知道让我们传入的到底是什么样的参数呢?
  • 但是,如果我们可以给JavaScript加上很多限制,在开发中就可以很好的避免这样的问题了:
    • 比如我们的getLength函数中str是一个必传的类型,没有调用者没有传编译期间就会报错;
    • 比如我们要求它的必须是一个String类型,传入其他类型就直接报错;
    • 那么就可以知道很多的错误问题在编译期间就被发现,而不是等到运行时再去发现和修改;

类型思维的缺失

  • 我们已经简单体会到没有类型检查带来的一些问题,JavaScript因为从设计之初就没有考虑类型的约束问题,所以造成了前端开发人员关于类型思维的缺失:
    • 前端开发人员通常不关心变量或者参数是什么类型的,如果在必须确定类型时,我们往往需要使用各种判断验证;
    • 从其他方向转到前端的人员,也会因为没有类型约束,而总是担心自己的代码不安全,不够健壮;
  • 所以我们经常会说JavaScript不适合开发大型项目,因为当项目一旦庞大起来,这种宽松的类型约束会带来非常多的安全隐患,多人员开发它们之间也没有良好的类型契约。
    • 比如当我们去实现一个核心类库时,如果没有类型约束,那么需要对别人传入的参数进行各种验证来保证我们代码的健壮性;
    • 比如我们去调用别人的函数,对方没有对函数进行任何的注释,我们只能去看里面的逻辑来理解这个函数需要传入什么参数,返回值是什么类型;

JavaScript添加类型约束

  • 为了弥补JavaScript类型约束上的缺陷,增加类型约束,很多公司推出了自己的方案:
    • 2014年,Facebook推出了flow来对JavaScript进行类型检查; p同年,Microsoft微软也推出了TypeScript1.0版本;
    • 他们都致力于为JavaScript提供类型检查;
  • 而现在,无疑TypeScript已经完全胜出:
    • Vue2.x的时候采用的就是flow来做类型检查;
    • Vue3.x已经全线转向TypeScript,98.3%使用TypeScript进行了重构;
    • 而Angular在很早期就使用TypeScript进行了项目重构并且需要使用TypeScript来进行开发;
    • 而甚至Facebook公司一些自己的产品也在使用TypeScript;
  • 学习TypeScript不仅仅可以为我们的代码增加类型约束,而且可以培养我们前端程序员具备类型思维。

介绍

认识 TypeScript

  • 虽然我们已经知道TypeScript是干什么的了,也知道它解决了什么样的问题,但是我们还是需要全面的来认识一下TypeScript到底是什么?
  • 我们来看一下TypeScript在GitHub和官方上对自己的定义:
    • GitHub说法:TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
    • TypeScript官网:TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
    • 翻译一下:TypeScript是拥有类型的JavaScript超集,它可以编译成普通、干净、完整的JavaScript代码。
  • 怎么理解上面的话呢?
    • 我们可以将TypeScript理解成加强版的JavaScript。 p JavaScript所拥有的特性,TypeScript全部都是支持的,并且它紧随ECMAScript的标准,所以ES6、ES7、ES8等新语法标准,它都是
      支持的;
    • 并且在语言层面上,不仅仅增加了类型约束,而且包括一些语法的扩展,比如枚举类型(Enum)、元组类型(Tuple)等;
    • TypeScript在实现新特性的同时,总是保持和ES标准的同步甚至是领先;
    • 并且TypeScript最终会被编译成JavaScript代码,所以你并不需要担心它的兼容性问题,在编译时也不需要借助于Babel这样的工具;
    • 所以,我们可以把TypeScript理解成更加强大的JavaScript,不仅让JavaScript更加安全,而且给它带来了诸多好用的好用特性;

TypeScript 特点

  • 官方对TypeScript有几段特点的描述,我觉得非常到位(虽然有些官方,了解一下),我们一起来分享一下:
  • 始于JavaScript,归于JavaScript
    • TypeScript从今天数以百万计的JavaScript开发者所熟悉的语法和语义开始。使用现有的JavaScript代码,包括流行的JavaScript库,并从JavaScript代码中调用TypeScript代码;
    • TypeScript可以编译出纯净、 简洁的JavaScript代码,并且可以运行在任何浏览器上、Node.js环境中和任何支持ECMAScript 3(或更高版本)的JavaScript引擎中;
  • TypeScript是一个强大的工具,用于构建大型项目
    • 类型允许JavaScript开发者在开发JavaScript应用程序时使用高效的开发工具和常用操作比如静态检查和代码重构;
    • 类型是可选的,类型推断让一些类型的注释使你的代码的静态验证有很大的不同。类型让你定义软件组件之间的接口和洞察现有JavaScript库的行为;
  • 拥有先进的 JavaScript
    • TypeScript提供最新的和不断发展的JavaScript特性,包括那些来自2015年的ECMAScript和未来的提案中的特性,比如异步功能和 Decorators,以帮助建立健壮的组件;
    • 这些特性为高可信应用程序开发时是可用的,但是会被编译成简洁的ECMAScript3(或更新版本)的JavaScript;

众多项目采用TypeScript

  • 正是因为有这些特性,TypeScript目前已经在很多地方被应用
    • Angular源码在很早就使用TypeScript来进行了重写,并且开发Angular也需要掌握TypeScript;
    • Vue3源码也采用了TypeScript进行重写,在前面阅读源码时我们看到大量TypeScript的语法;
    • 包括目前已经变成最流行的编辑器VSCode也是使用TypeScript来完成的;
    • 包括在React中已经使用的ant-design的UI库,也大量使用TypeScript来编写;
    • 目前公司非常流行Vue3+TypeScript、React+TypeScript的开发模式;
    • 包括小程序开发,也是支持TypeScript的;

大前端的发展趋势

  • 大前端是一群最能或者说最需要折腾的开发者:
    • 客户端开发者:从Android到iOS,或者从iOS到Android,到RN,甚至现在越来越多的客户端开发者接触前端相关知识(Vue、React、Angular、小程序);
    • 前端开发者:从jQuery到AngularJS,到三大框架并行:Vue、React、Angular,还有小程序,甚至现在也要接触客户端开发(比如RN、Flutter);
    • 目前又面临着不仅仅学习ES的特性,还要学习TypeScript;
    • 新框架的出现,我们又需要学习新框架的特性,比如vue3.x、react18等等;
  • 但是每一样技术的出现都会让惊喜,因为他必然是解决了之前技术的某一个痛点的,而TypeScript真是解决了JavaScript存在的很多设计缺陷,尤其是关于类型检测的。
  • 并且从开发者长远的角度来看,学习TypeScript有助于我们前端程序员培养 类型思维,这种思维方式对于完成大型项目尤为重要。

入门

TypeScript 编译环境

  • 在前面我们提到过,TypeScript最终会被编译成JavaScript来运行,所以我们需要搭建对应的环境:
    • 我们需要在电脑上安装TypeScript,这样就可以通过TypeScript的Compiler将其编译成JavaScript;
  • 所以,我们需要先可以先进行全局的安装:
    # 安装命令
    npm install typescript -g
    # 查看版本
    tsc --version
    

TypeScript的运行环境

  • 如果我们每次为了查看TypeScript代码的运行效果,都通过经过两个步骤的话就太繁琐了:
    • 第一步:通过tsc编译TypeScript到JavaScript代码;
    • 第二步:在浏览器或者Node环境下运行JavaScript代码;
  • 是否可以简化这样的步骤呢?
    • 比如编写了TypeScript之后可以直接运行在浏览器上?
    • 比如编写了TypeScript之后,直接通过node的命令来执行?
  • 上面我提到的两种方式,可以通过两个解决方案来完成:
    • 方式一:通过webpack,配置本地的TypeScript编译环境和开启一个本地服务,可以直接运行在浏览器上;
    • 方式二:通过ts-node库,为TypeScript的运行提供执行环境;
  • 方式一:webpack配置
    • 方式一在之前的TypeScript文章中我已经有写过,如果需要可以自行查看对应的文章;
    • https://mp.weixin.qq.com/s/wnL1l-ERjTDykWM76l4Ajw;
  • 方式二:安装ts-node
    • 另外ts-node需要依赖 tslib 和 @types/node 两个包:
    • 现在,我们可以直接通过 ts-node 来运行TypeScript的代码:
    • 使用ts-node
      npm install ts-node -g
      npm install tslib @types/node -g
      ts-node math.ts
      

变量的声明

  • 我们已经强调过很多次,在TypeScript中定义变量需要指定 标识符 的类型。
  • 所以完整的声明格式如下:
    • 声明了类型后TypeScript就会进行类型检测,声明的类型可以称之为类型注解;
      var/let/const 标识符: 数据类型 = 赋值;
      
  • 比如我们声明一个message,完整的写法如下:
    let message: string = 'Hello World'
    
    • 注意:这里的string是小写的,和String是有区别的
    • string是TypeScript中定义的字符串类型,String是ECMAScript中定义的一个类
  • 如果我们给message赋值其他类型的值,那么就会报错

声明变量的关键字

  • 在TypeScript定义变量(标识符)和ES6之后一致,可以使用var、let、const来定义。
  • 当然,在tslint中并不推荐使用var来声明变量:
    • 可见,在TypeScript中并不建议再使用var关键字了,主要原因和ES6升级后let和var的区别是一样的,var是没有块级作用域的,会引起很多的问题,这里不再展开探讨。
var name: string = 'why'
let age: number = 18
const height: number = 1.88

// string: TypeScript 中的字符串类型
// String: JavaScript 的字符串包装类型
const message: String = 'Hello World'

export {
   }

变量的类型推导(推断)

// 默认情况想进行赋值时,会将赋值的值的类型,作为前面标识符的类型
// 这个过程称之为类型推到/推断
// foo 没有添加类型注解
let foo = 'foo'
foo = 123 // 类型推断
  • 在开发中,有时候为了方便起见我们并不会在声明每一个变量时都写上对应的数据类型,我们更希望可以通过TypeScript本身的特性帮助我们推断出对应的变量类型
  • 如果我们给message赋值123:
  • 这是因为在一个变量第一次赋值时,会根据后面的赋值内容的类型,来推断出变量的类型:
    • 上面的message就是因为后面赋值的是一个string类型,所以message虽然没有明确的说明,但是依然是一个string类型;

数据类型

JavaScript类型

number类型

  • 数字类型是我们开发中经常使用的类型,TypeScript和JavaScript一样,不区分整数类型(int)和浮点型(double),统一为number类型。
  • 如果你学习过ES6应该知道,ES6新增了二进制和八进制的表示方法,而TypeScript也是支持二进制、八进制、十六进制的表示:
    let num1: number = 100 // 十进制
    let num2: number = 0b100 // 二级制
    let num3: number = 0o100 // 八进制
    let num4: number = 0x100 // 十六进制
    console.log(num1, num2, num3, num4) // 100 4 64 256
    

boolean类型

  • boolean类型只有两个取值:true和false
    let flag: boolean = true
    flag = 10 > 30
    

string 类型

  • string类型是字符串类型,可以使用单引号或者双引号表示
  • 同时也支持ES6的模板字符串来拼接变量和字符串
    let message1: string = 'hello world'
    let message2:string = "Hello World"
    
    const name = 'why'
    const age = 18
    const height = 1.88
    let message3 = `name:${
           name} age:${
           age} height:${
           height}`
    console.log(message3)
    

Array类型

  • 数组类型的定义也非常简单,有两种方式
  • 如果添加其他类型到数组中,那么会报错
    // 确定一个事实:names 是一个数组类型,但是数组中存放的是是什么类型的元素呢
    // 不好的习惯:一个数组在TypeScript开发中,最好存放的数据类型是固定的(string)
    // 类型注解:type annotation
    const names1: Array<string> = [] // 不推荐(react jsx 中有冲突) <div></div>
    const names2: string[] = [] // 推荐
    
    // 在数组中存放不同的类型是不好的习惯
    // names.push('abc')
    // names.push(123) // 报错
    

Object类型

const info: object = {
   
  name: 'why',
  age: 18
}
info['name'] = 'coderwhy'
console.log(info['age'])
  • object对象类型可以用于描述一个对象
  • 但是从myinfo中我们不能获取数据,也不能设置数据

Symbol类型

  • 在ES5中,如果我们是不可以在对象中添加相同的属性名称的,比如下面的做法
    const info = {
         
      title: '程序员',
      title: '老师' // 报错
    }
    
  • 通常我们的做法是定义两个不同的属性名字:比如title1和title2
  • 但是我们也可以通过symbol来定义相同的名称,因为Symbol函数返回的是不同的值
    const title1 = Symbol('title1')
    const title2 = Symbol('title2')
    const info = {
         
      [title1]: '程序员',
      [title2]: '老师'
    }
    

null和undefined类型

const n1: null = null
const n2: undefined = undefined
  • 在 JavaScript 中,undefined 和 null 是两个基本数据类型。
  • 在TypeScript中,它们各自的类型也是undefined和null,也就意味着它们既是实际的值,也是自己的类型

TypeScript类型

any 类型

  • 在某些情况下,我们确实无法确定一个变量的类型,并且可能它会发生一些变化,这个时候我们可以使用any类型(类似于Dart语言中的dynamic类型)。
  • any类型有点像一种讨巧的TypeScript手段:
    • 我们可以对any类型的变量进行任何的操作,包括获取不存在的属性、方法
    • 我们给一个any类型的变量赋值任何的值,比如数字、字符串的值;
  • 如果对于某些情况的处理过于繁琐不希望添加规定的类型注解,或者在引入一些第三方库时,缺失了类型注解,这个时候我们可以使用any
    • 包括在Vue源码中,也会使用到any来进行某些类型的适配
    // 当进行一些类型断言 as any
    // 在不想给某些 javascript 添加具体的数据类型时(原生的Javascript代码是一样)
    
    let message: any = 'Hello World'
    message = 123
    message = true
    message = {
         }
    
    function foo(payload: any) {
         }
    
    console.log(message)
    

unknown类型

  • unknown是TypeScript中比较特殊的一种类型,它用于描述类型不确定的变量。
    function foo() {
          
      return 'abc'
    }
    
    function bar() {
         
      return 123
    }
    
    // unknow 类型只能复制给 any 和 unknow 类型
    // any 类型可以赋值给任意类型
    const flag = true
    let result: unknown
    if (flag) {
         
      result = foo()
    } else {
         
      result = bar()
    }
    
    console.log(result)
    

void类型

function sum(num1: number, num2: number): void {
   
  console.log(num1 + num2)
}

sum(20, 30)
  • void通常用来指定一个函数是没有返回值的,那么它的返回值就是void类型:
    • 我们可以将null和undefined赋值给void类型,也就是函数可以返回null或者undefined
  • 这个函数我们没有写任何类型,那么它默认返回值的类型就是void的,我们也可以显示的来指定返回值是void:

never类型

  • never 表示永远不会发生值的类型,比如一个函数:
    • 如果一个函数中是一个死循环或者抛出一个异常,那么这个函数会返回东西吗?
    • 不会,那么写void类型或者其他类型作为返回值类型都不合适,我们就可以使用never类型;
    function foo(): never {
         
    // 死循环
    while (true) {
         }
    }
    
    function bar(): never {
         
      throw new Error()
    }
    
    // 知乎:never 到底应用在什么场景?
    
    function handleMessage(message: string | number | boolean) {
         
      switch (typeof message) {
         
        case 'string':
          console.log('string处理方式处理message')
          break
        case 'number':
          console.log('number处理方式处理message')
          break
        case 'boolean':
          console.log('boolean处理方式处理boolean')
          break
        default:
          const check: never = message // 报错
      }
    }
    
    handleMessage('abc')
    handleMessage(123)
    
    // 张三
    handleMessage(true)
    

tuple类型

  • tuple是元组类型,很多语言中也有这种数据类型,比如Python、Swift等。
  • 那么tuple和数组有什么区别呢?
    • 首先,数组中通常建议存放相同类型的元素,不同类型的元素是不推荐放在数组中。(可以放在对象或者元组中)
    • 其次,元组中每个元素都有自己特性的类型,根据索引值获取到的值可以确定对应的类型;
    //  tuple 元组:多种元素的组合
    // ‘why’ 18 1.88
    
    // 1.素组的弊端
    // const info: any[] = ['why', 18, 1.88]
    // const name = info[0]
    // console.log(name.length)
    
    // 冗余
    // const infoObj = {
         
    //   name: 'why',
    //   age: 18,
    //   height: 1.88
    // }
    
    // 2. 元组的特点
    const info: [string, number, number] = ['why', 18, 1.88]
    
    const name = info[0]
    console.log(name.length)
    const age = info[1]
    console.log(age.length) // 报错
    
  • 那么tuple在什么地方使用的是最多的呢?
    • tuple通常可以作为返回的值,在使用的时候会非常的方便
    // hook: useState
    // const [counter, setCounter] = useState(10)
    
    function useState<T>(state: T) {
         
      // T 泛型
      let currentState = state
      const changeState = (newState: T) => {
         
        currentState = newState
      }
      // const arr: any[] = [currentState, changeState]
      // return arr
      const tupple: [T, (newState: T) => void] = [currentState, changeState]
      return tupple
    }
    
    const [
      counter, // number
      setCounter // (newState: number) => void
    
  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值