TypeScript 是开源的,TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。编译出来的 JavaScript 可以运行在任何浏览器上。TypeScript 编译工具可以运行在任何服务器和任何系统上

  • 问题: 什么是超集

超集是集合论的术语
说到超集,不得不说另一个,子集,怎么理解这两个概念呢,举个例子

如果一个集合A里面的的所有元素集合B里面都存在,那么我们可以理解集合A是集合B的子集,反之集合B为集合A的超集

现在我们就能理解为 Typescript 里包含了 Javascript 的所有特性,这也意味着我们可以将.js后缀直接命名为.ts文件跑到TypeScript的编绎系统中

Typescript 解决了什么问题

一个事物的诞生一定会有其存在的价值

那么 Typescript 的价值是什么呢?

回答这个问题之前,我们有必要先来了解一下 Typescript 的工作理念

本质上是在 JavaScript 上增加一套静态类型系统(编译时进行类型分析),强调静态类型系统是为了和运行时的类型检查机制做区分,TypeScript 的代码最终会被编译为 JavaScript

我们再回到问题本身,缩小一下范围,Typescript 创造的价值大部分是在开发时体现的(编译时),而非运行时,如

  • 强大的编辑器智能提示 (研发效率,开发体验)
  • 代码可读性增强 (团队协作,开发体验)
  • 编译时类型检查 (业务稳健,前端项目中Top10 的错误类型低级的类型错误占比达到70%)

    正文

    本篇文章作为 Vue3 源码系列前置篇章之一,Typescript 的科普文,主要目的为了大家在面对 Vue3 源码时不会显得那么不知所措,下来将介绍一些 Typescript 的基本使用

    变量申明

    基本类型

    let isDone: boolean = false
    let num: number = 1
    let str: string = 'vue3js.cn'
    let arr: number[] = [1, 2, 3] 
    let arr2: Array<number> = [1, 2, 3] // 泛型数组
    let obj: Object = {}
    let u: undefined = undefined;
    let n: null = null;

    类型补充

    • 枚举 Enum

    使用枚举类型可以为一组数值赋予友好的名字

    enum LogLevel {
      info = 'info',
      warn = 'warn',
      error = 'error',
    }
    • 元组 Tuple

    允许数组各元素的类型不必相同。比如,你可以定义一对值分别为 string和number类型的元组

    // Declare a tuple type
    let x: [string, number];
    // Initialize it
    x = ['hello', 10]; // OK
    // Initialize it incorrectly
    x = [10, 'hello']; // Error
    • 任意值 Any

    表示任意类型,通常用于不确定内容的类型,比如来自用户输入或第三方代码库

    let notSure: any = 4;
    notSure = "maybe a string instead";
    notSure = false; // okay, definitely a boolean
    • 空值 Void

    与 any 相反,通常用于函数,表示没有返回值

    function warnUser(): void {
        console.log("This is my warning message");
    }
    • 接口 interface

    类型契约,跟我们平常调服务端接口要先定义字段一个理

    如下例子 point 跟 Point 类型必须一致,多一个少一个也是不被允许的

    interface Point {
        x: number
        y: number
        z?: number
        readonly l: number
    }
    const point: Point = { x: 10, y: 20, z: 30, l: 40 }
    const point2: Point = { x: '10', y: 20, z: 30, l: 40 } // Error 
    const point3: Point = { x: 10, y: 20, z: 30 } // Error 
    const point4: Point = { x: 10, y: 20, z: 30, l: 40, m: 50 } // Error 
     

    可选与只读 ? 表示可选参, readonly 表示只读

    const point5: Point = { x: 10, y: 20, l: 40 } // 正常
    point5.l = 50 // error

    函数参数类型与返回值类型

    function sum(a: number, b: number): number {
        return a + b
    }

    配合 interface 使用

    interface Point {
        x: number
        y: number
    } function sum({ x,  y}: Point): number {
        return x + y
    } sum({x:1, y:2}) // 3

    泛型

    泛型的意义在于函数的重用性,设计原则希望组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型

    • 比如

    根据业务最初的设计函数 identity 入参为String

    function identity(arg: String){
     return arg
    }
    console.log(identity('100'))

    业务迭代过程参数需要支持 Number

    function identity(arg: String){
     return arg
    }
    console.log(identity(100)) // Argument of type '100' is not assignable to parameter of type 'String'.

    为什么不用any呢?

    使用 any 会丢失掉一些信息,我们无法确定返回值是什么类型
    泛型可以保证入参跟返回值是相同类型的,它是一种特殊的变量,只用于表示类型而不是值

    语法 <T>(arg:T):T 其中T为自定义变量

    const hello : string = "Hello vue!"
    function say<T>(arg: T): T {
        return arg;
    }
    console.log(say(hello)) // Hello vue! 

    泛型约束

    我们使用同样的例子,加了一个console,但是很不幸运,报错了,因为泛型无法保证每种类型都有.length 属性

    const hello : string = "Hello vue!"
    function say<T>(arg: T): T {
     console.log(arg.length) // Property 'length' does not exist on type 'T'.
        return arg;
    }
    console.log(say(hello)) // Hello vue! 

    从这里我们也又看出来一个跟any不同的地方,如果我们想要在约束层面上就结束战斗,我们需要定义一个接口来描述约束条件

    interface Lengthwise {
        length: number;
    } function say<T extends Lengthwise>(arg: T): T {
     console.log(arg.length)
        return arg;
    }
    console.log(say(1))  // Argument of type '1' is not assignable to parameter of type 'Lengthwise'.
    console.log(say({value: 'hello vue!', length: 10})) // { value: 'hello vue!', length: 10 } 

    交叉类型

    交叉类型(Intersection Types),将多个类型合并为一个类型

    interface foo {
        x: number
    }
    interface bar {
        b: number
    }
    type intersection = foo & bar
    const result: intersection = {
        x: 10,
        b: 20
    }
    const result1: intersection = {
        x: 10
    }  // error

    联合类型

    交叉类型(Union Types),表示一个值可以是几种类型之一。我们用竖线 | 分隔每个类型,所以 number | string | boolean表示一个值可以是 number, string,或 boolean

    type arg = string | number | boolean
    const foo = (arg: arg):any =>{ 
        console.log(arg)
    }
    foo(1)
    foo('2')
    foo(true)

    函数重载

    函数重载(Function Overloading), 允许创建数项名称相同但输入输出类型或个数不同的子程序,可以简单理解为一个函数可以执行多项任务的能力

    例我们有一个add函数,它可以接收string类型的参数进行拼接,也可以接收number类型的参数进行相加

    function add (arg1: string, arg2: string): string
    function add (arg1: number, arg2: number): number // 实现
    function add <T,U>(arg1: T, arg2: U) {
      // 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 arg1 + arg2
      if (typeof arg1 === 'string' && typeof arg2 === 'string') {
        return arg1 + arg2
      } else if (typeof arg1 === 'number' && typeof arg2 === 'number') {
        return arg1 + arg2
      }
    } add(1, 2) // 3
    add('1','2') //'12'

    总结

    通过本篇文章,相信大家对Typescript不会再感到陌生了

    下面我们来看看在Vue源码Typescript是如何书写的,这里我们以defineComponent函数为例,大家可以通过这个实例,再结合文章的内容,去理解,加深Typescript的认识

    // overload 1: direct setup function
    export function defineComponent<Props, RawBindings = object>(
      setup: (
        props: Readonly<Props>,
        ctx: SetupContext
      ) => RawBindings | RenderFunction
    ): {
      new (): ComponentPublicInstance<
        Props,
        RawBindings,
        {},
        {},
        {},
        // public props
        VNodeProps & Props
      >
    } & FunctionalComponent<Props> // defineComponent一共有四个重载,这里省略三个 // implementation, close to no-op
    export function defineComponent(options: unknown) {
      return isFunction(options) ? { setup: options } : options
    }

Typescript | Vue3源码系列的更多相关文章

  1. 手牵手,从零学习Vue源码 系列一(前言-目录篇)

    系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 手牵手,从零学习Vue源码 系列三(虚拟DOM篇) 陆续更新中... 预计八月中旬更新 ...

  2. vue3源码难学,先从petite-vue开始吧

    如今这个世道,作为一个有几年工作经验的前端,不学点框架源码都感觉要被抛弃了,react或vue要能吹吹牛吧,最好能造个轮子,听说vue3源码好学点,那么学学vue3,但是学起来还是那么费劲,感觉快放弃 ...

  3. 事件机制-Spring 源码系列(4)

    事件机制-Spring 源码系列(4) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProcess ...

  4. Ioc容器依赖注入-Spring 源码系列(2)

    Ioc容器依赖注入-Spring 源码系列(2) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostPr ...

  5. Ioc容器BeanPostProcessor-Spring 源码系列(3)

    Ioc容器BeanPostProcessor-Spring 源码系列(3) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Io ...

  6. AOP执行增强-Spring 源码系列(5)

    AOP增强实现-Spring 源码系列(5) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProc ...

  7. 大白话Vue源码系列(02):编译器初探

    阅读目录 编译器代码藏在哪 Vue.prototype.$mount 构建 AST 的一般过程 Vue 构建的 AST 题接上文,上回书说到,Vue 的编译器模块相对独立且简单,那咱们就从这块入手,先 ...

  8. 大白话Vue源码系列(03):生成AST

    阅读目录 AST 节点定义 标签的正则匹配 解析用到的工具方法 解析开始标签 解析结束标签 解析文本 解析整块 HTML 模板 未提及的细节 本篇探讨 Vue 根据 html 模板片段构建出 AST ...

  9. 大白话Vue源码系列(03):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

随机推荐

  1. MyBatis辟邪剑谱

    一 MyBatis简介 MyBatis是一个优秀的持久层框架 它对JDBC操作数据库的过程进行封装 开发者只需要关注SQL本身 而不需要花费精力去处理JDBC繁杂的过程代码 MyBatis将要执行的各 ...

  2. 使用IDEA生成jar包的步骤(IDEA打jar包)

    第一步: 1.把module目录下的MATA-INF文件夹删除,如果没有MATA-INF文件夹则不用删除 2.Ctrl + Alt + Shift + S 打开 Project Structure 窗 ...

  3. Redis实现商品热卖榜

    Redis系列 redis相关介绍 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合 ...

  4. java进阶(2)--抽象类

    一.抽象类集刺虎: 1.概念 类与类之前有共同特征,将这些共同特征提取出来,行为抽象类   2.抽象类的数据类型: 引用数据类型   3.语法 [修饰符列表]abstract Class 类型{类体} ...

  5. Dropzone.js文件拖拽上传提示Dropzone already attached 解决

    最近收到客户的反馈,在操作上传文件有时会出现没有任何.大部分时间是正常. 重现问题后,f12打开后台控制台发现如下提示: Uncaught Error: Dropzone already attach ...

  6. python3.5 continue和break 项目:买房分期付款(2)

    #案例:买房分期付款24万(10年期限) i=1#定义年份sum1=0while i<=10: print("第",i,"年到了......") if i ...

  7. Python3.x+Fiddler抓取APP数据

    随着移动互联网的市场份额逐步扩大,手机APP已经占据我们的生活,以往的数据分析都借助于爬虫爬取网页数据进行分析,但是新兴的产品有的只有APP,并没有网页端这对于想要提取数据的我们就遇到了些问题,本章以 ...

  8. java中ThrealLocal的理解

    目录 java中threadlocal的理解 一.threadlocal的生命周期和ThreadLocalMap的生命周期 二.ThreadLocal的作用 三.threadlocal示例 四.Inh ...

  9. .NET Core Web APi大文件分片上传研究

    前言 前两天发表利用FormData进行文件上传,然后有人问要是大文件几个G上传怎么搞,常见的不就是分片再搞下断点续传,动动手差不多也能搞出来,只不过要深入的话,考虑的东西还是很多.由于断点续传之前写 ...

  10. Docker容器网络-实现篇

    通常,Linux容器的网络是被隔离在它自己的Network Namespace中,其中就包括:网卡(Network Interface).回环设备(Loopback Device).路由表(Routi ...