Skip to content

Vue3 + Typescript 从0到1开发通用基础组件

1. 什么是 TypeScript

TypeScript相关手册

https://www.typescriptlang.org/zh/

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript

安装 Typescript

shell
$ node -v
v14.19.1

$ npm -v
6.14.16

$ npm install -g typescript
D:\data\nodejs\node_global\tsc -> D:\data\nodejs\node_global\node_modules\typescript\bin\tsc
D:\data\nodejs\node_global\tsserver -> D:\data\nodejs\node_global\node_modules\typescript\bin\tsserver
+ typescript@4.7.4
added 1 package from 1 contributor in 2.251s

$ tsc -v
Version 4.7.4

# 可选补充安装
$ npm install -g ts-node

手写一个Demo

  • 创建app.ts
typescript
var hello = (name: string) => {
    return `hello ${name}`
}

hello('world')
  • 运行ts编译命令
shell
$ tsc app.ts

$ ls
app.js  app.ts

$ cat app.js
var hello = function (name) {
    return "hello ".concat(name);
};
hello('world');
  • 更改保存 app.ts(编译错误的语法)
typescript
var hello = (name: string) => {
    return `hello ${name}`
}

hello(123)
  • 再次运行ts编译命令
shell
$ tsc app.ts
app.ts:5:7 - error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.

5 hello(123)
        ~~~


Found 1 error in app.ts:5

此时,依然能正常生成app.js。需要自行排查问题

2. 原始数据类型和 Any 类型

  • 创建basic-types.ts
typescript
let isDone: boolean = false
let age: number = 29
let firstName: string = 'lintong'
let message: string = `Hello, ${firstName}`

let u: undefined = undefined
let n: null = null

let notSure: any = 4
notSure = 'maybe a string'
notSure = true

notSure.myName
notSure.getName()

3. 数组和元祖

Tuple 元祖:元祖的表示和数组非常类似,只不过它将类型写在了里面 这就对每一项起到了限定的作用

  • 创建array-and-tuple.ts
typescript
let arrOfNumbers: number[] = [1, 2, 3]
arrOfNumbers.push(4)
// push除number以外的会报错提示
// arrOfNumbers.push('hello')

function test() {
    console.log(arguments)  // 类数组
    // 以下被允许
    let length = arguments.length
    let arr0 = arguments[0]
    console.log(length, arr0)

    // 以下都不被允许
    // arguments.forEach
    // let arr: any[] = arguments
}

test()

let user: [string, number] = ['lintong', 29]
// 添加string、number类型则正常push
user.push(30)
// 会报错
// user.push(true)

4. Interface-接口

对对象的形状(shape)进行描述
Duck Typing(鸭子类型)

  • 创建interface-basic.ts
typescript
interface Person {
    readonly id: number
    name: string
    age?: number    // ?:可选参数。实例化时不会检测其是否存在该属性
}

let lintong: Person = {
    id: 1,
    name: 'lintong',
    age: 29
}
// lintong.id = 2  // 只读属性,只能被赋值一次,之后不能再被更改

5. 函数

在 JS 中,函数是一等公民。

  • 创建function-types.ts
typescript
const add = (x: number, y: number, z?: number): number => {
    if (typeof z === 'number') {
        return x + y + z
    }
    return x + y
}

let result1 = add(1, 2)
let result2 = add(1, 2, 3)

// let result3 = add(1, '2')   // 报错

// let add2: number = add  // 报错
let add3: (x: number, y: number, z?: number) => number = add    // 正确

// 等同于 add3
interface ISum {
    (x: number, y: number, z?: number): number
}

let add4: ISum = add

6. 类型推论 联合类型和类型断言

类型推论 - 没有明确的指定类型的时候推测出一个类型
联合类型 - 表示类型或的关系
类型断言 - 类型断言用来告诉编译器你比它更了解这个类型。不是类型转换,断言成一个联合类型中不存在的类型是会出现错误的
类型守卫 - 当遇到一个联合类型的时候,使用条件语句,它可以自动帮你来缩小类型的范围:typeof 和 instanceof 关键字

  • 创建type-interface-and-more.ts
typescript
// type interface
let str = 'str'
// str = 123   // 报错

// union types
let numberOrString: number | string
numberOrString = 'abc'
numberOrString = 123
// 访问某一个类型独有的属性会报错
// numberOrString.length   // 报错
// 访问共有属性则没问题
numberOrString.toString()
numberOrString.valueOf()

function getLength(input: string | number): number {
    const str = input as string
    if (str.length) {
        return str.length
    } else {
        const number = input as number
        return number.toString().length
    }
}

// type guard
// 改写 getLength 函数
function getLength2(input: string | number): number {
    if (typeof input === 'string') {
        return str.length
    } else {
        return input.toString().length
    }
}

7. 枚举(Enum)

  • 创建enums.ts
typescript
enum Direction {
    Up,
    Down,
    Left = 10,
    Right,
}

console.log(Direction.Up)   // 0
console.log(Direction[0])   // Up

console.log(Direction[10])   // Left
console.log(Direction.Right)   // 11
  • 运行ts编译命令
shell
$ tsc enums.ts

$ cat enums.js 
var Direction;
(function (Direction) {
    Direction[Direction["Up"] = 0] = "Up";
    Direction[Direction["Down"] = 1] = "Down";
    Direction[Direction["Left"] = 10] = "Left";
    Direction[Direction["Right"] = 11] = "Right";
})(Direction || (Direction = {}));
console.log(Direction.Up); // 0
console.log(Direction[0]); // Up
console.log(Direction[10]); // Left
console.log(Direction.Right); // 11
  • 使用场景1
typescript
enum Direction {
    Up = 'UP',
    Down = 'Down',
    Left = 'Left',
    Right = 'Right',
}

const value = 'UP'
if (value === Direction.Up) {
    console.log('go up!')
}
  • 运行ts编译命令
shell
$ tsc enums.ts

$ cat enums.js 
var Direction;
(function (Direction) {
    Direction["Up"] = "UP";
    Direction["Down"] = "Down";
    Direction["Left"] = "Left";
    Direction["Right"] = "Right";
})(Direction || (Direction = {}));
var value = 'UP';
if (value === Direction.Up) {
    console.log('go up!');
}

改造后(编译后的代码更简单,性能更好)

typescript
const enum Direction {}
  • 重新运行ts编译命令
shell
$ cat enums.js 
var value = 'UP';
if (value === "UP" /* Direction.Up */) {
    console.log('go up!');
}

8.泛型(Generics)

  • 创建generics.ts
typescript
function echo<T>(arg: T): T {
    return arg
}

const result = echo(true)

function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]]
}

const result2 = swap(['string', 123])

约束泛型

typescript
function echoWithArr<T>(arg: T[]): T[] {
    console.log(arg.length)
    return arg
}

const arrs = echoWithArr([1, 2, 3])
typescript
interface IWithLength {
    length: number
}

function echoWithLength<T extends IWithLength>(arg: T): T {
    console.log(arg.length)
    return arg
}

const str = echoWithLength('str')
const obj = echoWithLength({length: 10, width: 20})
const arr = echoWithLength([1, 2, 3])
// echoWithLength(13)  // 报错,因为整型13不存在length属性,约束泛型报错

泛型在类和接口中的使用

  • 泛型在类中的使用
typescript
class Queue<T> {
    private data = []

    push(item: T) {
        return this.data.push(item)
    }

    pop(): T | undefined {
        return this.data.shift()
    }
}

const queue = new Queue<number>()
queue.push(1)
console.log(queue.pop()?.toFixed())
  • 泛型在接口中的使用
typescript
interface KeyPair<T, U> {
    key: T,
    value: U
}

let kp1: KeyPair<number, string> = {key: 1, value: 'string'}
let kp2: KeyPair<string, number> = {key: 'str', value: 2}
let arr: number[] = [1, 2, 3]
let arr2: Array<number> = [1, 2, 3]

9. 类型别名,字面量 和 交叉类型

类型别名 - 给类型起一个快捷方式类型的名称
交叉类型 - 将几种类型合并起来

  • 创建type-alias.ts
typescript
// 类型别名
let sum: (x: number, y: number) => number
const result = sum(1, 2)
type PlusType = (x: number, y: number) => number
let sum2: PlusType
const result2 = sum2(2, 3)
type StrOrNumber = string | number
let result3: StrOrNumber = '123'
result3 = 123
// result3 = true  // 报错

// 字面量
const str: 'name' = 'name'
// const str: 'name' = 'name2' // 报错,字面量,值只能是约束的值(此处只能为‘name’)
const number: 1 = 1 // 一般用于定义常量,约束常量对应的值
type Directions = 'Up' | 'Down' | 'Left' | 'Right'
let toWhere: Directions = 'Right'

// 交叉类型
interface IName {
    name: string
}

type IPerson = IName & { age: number }
let person: IPerson = {name: 'lintong', age: 29}

10. 内置类型

  • 创建build-in-types.ts
typescript
// global objects
const a: Array<number> = [1, 2, 3]
const date = new Date()
date.getTime()
const reg = /abc/
reg.test('abc')

// build-in object
Math.pow(2, 2)

// DOM and BOM
let body = document.body
let allLis = document.querySelectorAll('li')    // 获取文档中所有li标签
allLis.keys()

// 注册一个点击事件
document.addEventListener('click', (e) => {
    e.preventDefault()
})

// Utility Types
interface IPerson {
    name: string
    age: number
}

let lintong: IPerson = {name: 'liintong', age: 29}
type IPartial = Partial<IPerson>
let lintong2: IPartial = {name: 'lintong'}
type IOmit = Omit<IPerson, 'name'>
let lintong3: IOmit = {age: 29}

11. TS配置文件 tsconfig.json

最近更新