重学vue(2, 3)及其生态+TypeScript 之 TypeScript(上)

简介: 重学vue(2, 3)及其生态+TypeScript 之 TypeScript

编译TypeScript


我们知道typescript只有编译程javascript时,才能在node或者浏览器上运行,那么如何编译typescript呢?


  • 通过tsc命令。


  • 如果想要执行该命令,我们需要全局安装typescript。这样就可以使用该命令编译ts文件了。他会生成对应名字的js文件,然后我们可以通过node或者浏览器运行该js文件。


  • 通过ts-node库,来将ts文件直接在node中进行运行。


  • 安装ts-node库 npm install ts-node -g


  • 并且它还依赖两个其他的库tslib, @types/node。所以我们可以我们也需要安装npm install tslib @types/node -g


  • 通过webpack配置相关loader来编译ts文件。 需要安装一下这些库


"html-webpack-plugin": "^5.3.2",
    "ts-loader": "^9.2.3",
    "typescript": "^4.3.5",
    "webpack": "^5.44.0",
    "webpack-cli": "^4.7.2",
    "webpack-dev-server": "^3.11.2"


并且需要执行tsc --init, 来生成'tsconfig.json'文件,不然编译时会报错。 在package.json文件中配置一下脚本。


"scripts": {
    "build": "webpack",
    "serve": "webpack serve"
  }


webpack的配置


const path = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    module.exports = {
      mode: "development",
      entry: "./src/main.ts",
      output: {
        path: path.resolve(__dirname, "./dist"),
        filename: "bundle.js"
      },
      devServer: {
      },
      resolve: {
        extensions: [".ts", ".js", ".cjs", ".json"]
      },
      module: {
        rules: [
          {
            test: /\.ts$/,
            loader: 'ts-loader'
          }
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: "./index.html"
        })
      ]
    }


执行npm run serve运行项目即可。


TypeScript类型


TypeScript是JavaScript的一个超级。js中存在的类型在ts中都存在,并且ts也扩展了自己的类型,下面我们就来看一下吧。需要注意的是基本类型的值可以赋值给对应的包装类型,但是包装类型不能赋值给对应的基本类型。


const message1: String = 'HelloWorld'// 这里是可以的
    const message2: string = new String('Hello World') //这里会报错


any类型:unknown是TypeScript中比较特殊的一种类型,它用于描述类型不确定的变量。


unknown类型只能赋值给any和unknown类型 在某些情况下,我们确实无法确定一个变量的类型,并且可能它会发生一些变化,这个时候我们可以使用any类型(类似于Dart语言中的dynamic类型)。


我们给一个any类型的变量赋值任何的值,比如数字、字符串的值


unknown类型


unknown是TypeScript中比较特殊的一种类型,它用于描述类型不确定的变量。


unknown类型只能赋值给any和unknown类型。any类型可以赋值给任意类型。所以unknown相比于any更安全。


function foo() {
      return 'abc'
    }
    function bar() {
      return 123
    }
    // unknown类型只能赋值给any和unknown类型
    // any类型可以赋值给任意类型
    let flag = true
    // 接收的返回值可能是string, 也可能是number。
    let result: unknown // 最好不要使用any
    if (flag) {
      result = foo()
    } else {
      result = bar()
    }


void类型


  • void通常用来指定一个函数是没有返回值的,那么它的返回值就是void类型。


  • 我们可以将null和undefined赋值给void类型,也就是函数可以返回null或者undefined。


  • 函数我们没有写任何类型,那么它默认返回值的类型就是void的,我们也可以显示的来指定返回值是void。


function sum(num1: number, num2: number): void {
      console.log(num1 + num2)
      return 'pp' // 如果指定了函数没有返回值,这里会报错。但是可以返回undefined, null。
    }
    sum(20, 30)


never类型


never 表示永远不会发生值的类型。


比如一个函数:如果一个函数中是一个死循环或者抛出一个异常,那么这个函数会返回东西吗?


不会,那么写void类型或者其他类型作为返回值类型都不合适,我们就可以使用never类型。


tuple类型


tuple是元组类型,很多语言中也有这种数据类型,比如Python、Swift等。


那么tuple和数组有什么区别呢?


  • 首先,数组中通常建议存放相同类型的元素,不同类型的元素是不推荐放在数组中。(可以放在对象或者元组中)。


  • 其次,元组中每个元素都有自己特性的类型,根据索引值获取到的值可以确定对应的类型。


  • 其实他就是固定长度和类型的数组。


const info: [string, number, number] = ["zh", 20, 0]
    const name = info[0]
    console.log(info.slice(0, 1))


大致了解了tuple,那他将用在什么场景呢?通过下面例子来看一看吧。


简单实现一个useState hook函数,我们都知道useState hook,但会一个数组,其中包括两个元素,第一个元素是传入的值,第二个元素是一个函数,所以我们就可以使用tuple来对其进行约束。


function useState<T>(state: T) {
      let currentState = state
      const changeState = (newState: T) => {
        currentState = newState
      }
      // 约束返回值
      const tuple: [T, (newState: T) => void] = [currentState, changeState]
      return tuple
    }
    const [counter, setCounter] = useState(10);
    setCounter(1000)
    const [title, setTitle] = useState("abc")


函数的类型约束


函数是JavaScript非常重要的组成部分,TypeScript允许我们指定函数的参数和返回值的类型。


函数类型的定义


type FooFnType = () => void
    function bar(fn: FooFnType) {
      fn()
    }


参数的类型注解


  • 声明函数时,可以在每个参数后添加类型注解,以声明函数接受的参数类型。


  • 当我们在定义函数参数的时候,必选参数不能位于可选参数后。


// 会报错
    function foo(n1?: number, n2: string) {
      return n1 + n2
    }
    foo(9, 'pp')
    // 但是对于定义对象类型的属性约束的时候,我们可以将可选参数写在任意位置,只是代表该属性可以不提供而已
    type objType = {
      name?: string
      age: number
    }


  • 当出现在高阶函数中的传入的函数参数,我们不需要指定类型注解,因为该函数的参数会自动指定类型


const names = ["abc", "cba", "nba"]
    // item根据上下文的环境推导出来的, 这个时候可以不添加的类型注解
    // 上下文中的函数: 可以不添加类型注解
    names.forEach(function(item) {
    })


  • 当传入的类型是一个对象的时候


  • 我们可以这样指定


function printPoint(point: { x: number; y: number }) {
  console.log(point.x)
  console.log(point.y)
}
printPoint({ x: 123, y: 321 })


  • 我们也可以指定对象属性的可选性,通过?来标识


// 这里表示我们可以不传入z属性。
function printPoint(point: {x: number, y: number, z?: number}) {
  console.log(point.x)
  console.log(point.y)
  console.log(point.z)
}
printPoint({x: 123, y: 321})
printPoint({x: 123, y: 321, z: 111})


返回值的类型注解


  • 声明函数时, 可以在函数列表的后面添加返回值类型注解。


  • 和变量的类型注解一样,我们通常情况下不需要返回类型注解,因为TypeScript会根据 return 返回值推断函数的返回类型。 函数重载


函数重载的定义:


允许创建多个具有不同实现的同名函数。对重载函数的调用会运行其适用于调用上下文的具体实现,即允许一个函数调用根据上下文执行不同的任务。


  • 多个函数定义使用相同的函数名称


  • 函数参数的数量或类型必须有区别 在ts中实现函数重载和其他编程语言不同,它需要先定义函数的重载类型,然后再实现函数。


下面这是错误的实现重载的方式


// 错误实现重载函数
    function p(n1: number, n2: number) {
      return n1 + n2
    }
    function p(n1: string, n2: string) {
      return n1 + n2
    }
    console.log(p(20, 30))


正确实现重载的方式


// 定义函数的重载类型
    // 函数的重载: 函数的名称相同, 但是参数不同的几个函数, 就是函数的重载
    function add(num1: number, num2: number): number; // 没函数体
    function add(num1: string, num2: string): string;
    // 实现重载函数的内部逻辑
    function add(num1: any, num2: any): any {
      if (typeof num1 === 'string' && typeof num2 === 'string') {
        return num1.length + num2.length
      }
      return num1 + num2
    }
    const result = add(20, 30)
    const result2 = add("abc", "cba")


联合类型


有时候一个变量,他可以被赋值为多种类型的值,我们就可以使用|来分割每种类型。


const id: number|string|boolean = 2;
    console.log('id', id)


其实上面讲到的可选类型,可以看做是 类型undefined 的联合类型


message?: string ===> message: string | undefined


交叉类型


交叉类型表示需要满足多个类型的条件。我们就可以使用 & 来分割每种类型。


interface ISwim {
      swimming: () => void
    }
    interface IFly {
      flying: () => void
    }
    type MyType1 = ISwim | IFly 
    type MyType2 = ISwim & IFly
    // 如果只实现了一个接口的方法,我们可以直接调用该方法,ts自动推断了类型。
    const obj1: MyType1 = {
      // 这里可以实现两个接口,或者只实现其中一个接口
      // flying() {},
      swimming() {},
    }
    const obj2: MyType2 = {
      // 两个接口的方法都得实现
      swimming() {},
      flying() {},
    }


在开发中,我们进行交叉时,通常是对对象类型进行交叉的。合并对象


interface Colorful {
  color: string
}
interface IRun {
  running: () => void
}
type NewType = Colorful & IRun
const obj: NewType = {
  color: 'red',
  running: () => {},
}


类型别名


有时候,我们写对象或者比较复杂的类型时。例如传入给一个函数作为参数,看起来比较不好理解,而且复杂。当多个地方都用到同样的对象结构的时候,我们需要写很多遍。所以,这时候我们就可以使用type来为对象定义一个类型别名。让其可复用且容读。


type IDType = string | number | boolean
    type PointType = {
      x: number
      y: number
      z?: number
    }
    function printId(id: IDType) {
    }
    function printPoint(point: PointType) {
    }


类型断言


有时候TypeScript无法获取具体的类型信息,这个我们需要使用类型断言(Type Assertions)。一般用在类中比较多。


class Person {
    }
    class Student extends Person {
      studying() {
      }
    }
    function sayHello(p: Person) {
    // 将Person类型转为Student类型
      (p as Student).studying()
    }
    const stu = new Student()
    sayHello(stu)


TypeScript只允许类型断言转换为 更具体 或者 不太具体 的类型版本,此规则可防止不可能的强制转换。


网络异常,图片无法展示
|


非空类型断言!


当我们编写下面的代码时,在执行ts的编译阶段会报错:这是因为传入的message有可能是为undefined的,这个时候是不能执行方法的;


function printMessageLength(message?: string) {
  console.log(message.length)
}
printMessageLength()


但是,我们确定传入的参数是有值的,这个时候我们可以使用非空类型断言:非空断言使用的是 ! ,表示可以确定某个标识符是有值的,跳过ts在编译阶段对它的检测。如果没有传值,执行是依旧报错。


function printMessageLength(message?: string) {
  console.log(message!.length)
}
printMessageLength() // 依旧报错,只有我们调用函数都传入值的时候,才不会报错。


这时候,我们就可以使用es11中可选链使用可选链操作符 ?., 它的作用是当对象的属性不存在时,会短路,直接返回undefined,如果存在,那么才会继续执行。


function printMessageLength(message?: string) {
  console.log(message?.length)
}
printMessageLength() // 不会报错,返回undefined。


既然讲到了?.,那我们就再来看看??的作用吧?


他是es11新增的。空值合并操作符(??)是一个逻辑操作符,当操作符的左侧是 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。其实他的效果和||一样,但是||的使用本身用在if判断中的,所以以后可以用??来代替||做短路运算。


let message: string | null = 'Hello World'
    const content1 = message ?? '你好1'
    const content2 = message ? message : '你好2'
    const content3 = message || '你好3'
    console.log(content1)
    console.log(content2)
    console.log(content3)


字面量类型


除了前面我们所讲过的类型之外,也可以使用字面量类型(literal types)。这个类型再能赋值指定的字面量作为值。 他一般和联合类型使用才有意义。


// 字面量类型的意义, 就是必须结合联合类型
    type Alignment = 'left' | 'right' | 'center'
    let align: Alignment = 'left'
    align = 'right'
    align = 'center'


类型缩小


什么是类型缩小呢?


我们可以通过类似于 typeof padding === "number" 的判断语句,来改变TypeScript的执行路径。在给定的执行路径中,我们可以缩小比声明时更小的类型,这个过程称之为 缩小。而我们编写的 typeof padding === "number 可以称之为 类型保护(type guards)。


常见的类型保护有如下几种:


  • typeof


// 1.typeof的类型缩小
    type IDType = number | string
    function printID(id: IDType) {
      if (typeof id === 'string') {
        console.log(id.toUpperCase())
      } else {
        console.log(id)
      }
    }


  • 平等缩小(比如===、!==)


type Direction = 'left' | 'right' | 'top' | 'bottom'
    function printDirection(direction: Direction) {
      // 1.if判断
      if (direction === 'left') {
        console.log(direction)
      }else if() {
        ...
      }
      // 2.switch判断
      switch (direction) {
        case 'left':
          console.log(direction)
          break
        case 'right':
          console.log(direction)
          break
        case 'top':
          console.log(direction)
          break
        case 'bottom':
          console.log(direction)
          break
      }
    }


  • instanceof, 一般用于类中的判断


class Student {
      studying() {}
    }
    class Teacher {
      teaching() {}
    }
    function work(p: Student | Teacher) {
      if (p instanceof Student) {
        p.studying()
      } else {
        p.teaching()
      }
    }
    const stu = new Student()
    work(stu)


  • in


// 4. in
    type Fish = {
      swimming: () => void
    }
    type Dog = {
      running: () => void
    }
    function walk(animal: Fish | Dog) {
      if ('swimming' in animal) {
        animal.swimming()
      } else {
        animal.running()
      }
      // (animal as Fish).swimming()
    }
    const fish: Fish = {
      swimming() {
        console.log('swimming')
      },
    }
    walk(fish)


相关文章
|
2月前
|
JavaScript 前端开发 安全
【技术革新】Vue.js + TypeScript:如何让前端开发既高效又安心?
【8月更文挑战第30天】在使用Vue.js构建前端应用时,结合TypeScript能显著提升代码质量和开发效率。TypeScript作为JavaScript的超集,通过添加静态类型检查帮助早期发现错误,减少运行时问题。本文通过具体案例展示如何在Vue.js项目中集成TypeScript,并利用其类型系统提升代码质量。首先,使用Vue CLI创建支持TypeScript的新项目,然后构建一个简单的待办事项应用,通过定义接口描述数据结构并在组件中使用类型注解,确保代码符合预期并提供更好的编辑器支持。
72 0
|
2月前
|
JavaScript 前端开发 安全
立等可取的 Vue + Typescript 函数式组件实战
立等可取的 Vue + Typescript 函数式组件实战
|
3月前
|
JavaScript 前端开发
【Vue3+TypeScript】CRM系统项目搭建之 — 关于如何设计出优质的 Vue 业务组件
【Vue3+TypeScript】CRM系统项目搭建之 — 关于如何设计出优质的 Vue 业务组件
42 0
【Vue3+TypeScript】CRM系统项目搭建之 — 关于如何设计出优质的 Vue 业务组件
|
4月前
|
JavaScript 安全 前端开发
Vue 3 中的 TypeScript
【6月更文挑战第15天】
75 6
|
5月前
|
JavaScript 前端开发 开发者
类型检查:结合TypeScript和Vue进行开发
【4月更文挑战第24天】TypeScript是JavaScript超集,提供类型注解等特性,提升代码质量和可维护性。Vue.js是一款高效前端框架,两者结合优化开发体验。本文指导如何配置和使用TypeScript与Vue:安装TypeScript和Vue CLI,创建Vue项目时选择TypeScript支持,配置`tsconfig.json`,编写`.tsx`组件,最后运行和构建项目。这种结合有助于错误检查和提升开发效率。
50 2
|
5月前
|
JavaScript 前端开发 开发者
Vue工具和生态系统: Vue.js和TypeScript可以一起使用吗?
【4月更文挑战第18天】Vue.js与TypeScript兼容,官方文档支持在Vue项目中集成TypeScript。TypeScript作为JavaScript超集,提供静态类型检查和面向对象编程,增强代码准确性和健壮性。使用TypeScript能提前发现潜在错误,提升代码可读性,支持接口和泛型,使数据结构和函数更灵活。然而,不是所有Vue插件都兼容TypeScript,可能需额外配置。推荐尝试在Vue项目中使用TypeScript以提升项目质量。
107 0
|
5月前
|
JavaScript 前端开发
在Vue中使用TypeScript的常见问题有哪些?
在Vue中使用TypeScript的常见问题有哪些?
89 2
|
2天前
|
缓存 JavaScript 前端开发
《基础篇第4章:vue2基础》:使用vue脚手架创建项目
《基础篇第4章:vue2基础》:使用vue脚手架创建项目
10 3
|
4天前
|
JavaScript 前端开发 开发者
Vue v-for 进阶指南:in 与 of 的区别及应用场景 | 笔记
Vue.js 中的 v-for 是强大的遍历指令,但其中的 in 和 of 关键字往往被开发者忽视。尽管它们的用法相似,但适用的场景和数据结构却各有不同。本文将详细探讨 v-for 中 in 和 of 的区别、适用场景以及在实际开发中的最佳使用时机。通过理解它们的差异,你将能够编写更加高效、简洁的 Vue.js 代码,灵活应对各种数据结构的遍历需求。
40 6
|
2天前
|
缓存 JavaScript
Vue 中 computed 与 method 的区别
【10月更文挑战第15天】computed 和 method 是 Vue 中两个重要的选项,它们在功能和特点上存在着明显的区别。理解并合理运用它们的区别,可以帮助我们构建更高效、更具可维护性的 Vue 应用。在实际开发中,要根据具体情况灵活选择使用,以满足不同的需求。
5 2