likes
comments
collection
share

从零单排番外篇:给JavaScript加点“类型”

作者站长头像
站长
· 阅读数 14

背景

公司计划将新项目使用vue3+ts开发,新项目需要引入公司的基础库,但是基础库是用js写的且没有类型声明的文件,直接使用ts会报无法找到模块的错误,为了解决报错问题就需要我们自行为基础库加上声明文件

什么是声明文件

声明文件 也 叫做描述文件,以d.ts结尾的文件名。声明文件主要是ts编译器 和 编辑器使用的

  • ts编译器-项目中使用到js文件,编译时能进行类型校验
  • 编辑器-使用js文件时也有智能提示

第三方库的声明文件

我们知道目前还有很多第三方库是使用js写的例如express,随着ts的火热,很多常用的第三方库都有相应的类型声明文件,一部分是库的作者自己添加的(axios库自带类型声明文件),一部分是有DefinitelyTyped提供

  • DefinitelyTyped 是一个 github 仓库,初衷是为没有声明文件的第三方库提供可靠的声明文件,由微软牵头、社区的共同努力为大多数常用的第三方库都提供了声明文件,名称格式为:@types/*,例如:@types/express,当安装 @types/* 类型声明包后,TS 就会自动加载该类声明包,以提供该库的类型声明,感兴趣的可以查看仓库地址

从零单排番外篇:给JavaScript加点“类型”

从零单排番外篇:给JavaScript加点“类型”

自定义的声明文件

比如作者目前遇到的情况,之前的库代码或者封装好的方法使用的js编写的, 现在需要在ts的项目上使用的话要么使用ts重写库的代码,要么自定义类型文件,考虑到时间成本的话,第二个方法也是不错的选择

自定义声明文件主要使用 declare 关键字,它的作用是为已存在的变量声明类型

  • 对于 type、interface 等这些明确就是 TS 类型的(只能在 TS 中使用的),可以省略 declare 关键字。
  • 对于 let、function 等具有双重含义(在 JS、TS 中都能用),就要使用declare明确表示此处用于类型声明

declare let 声明全局变量

declare function 声明全局方法

declare class 声明全局类

declare enum 声明全局枚举类型

declare namespace 声明(含有子属性的)全局对象

interface 和 type 声明全局类型

初始化项目

  • 新建test-types文件夹
  • cd到test-types文件夹下,执行pnpm --init初始化package.json,并在package.json添加 "type": "module"
pnpm --init
  • 安装typescript,执行tsc --init初始化tsconfig.json,并修改成如下内容,主要是告诉ts编译器,将ts编译成esmodule风格的代码和找声明文件的路径
pnpm add typescript -D
tsc --init
{
  "compilerOptions": {
    "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "module": "ESNext", /* Specify what module code is generated. */ /* Specify the root folder within your source files. */
    "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
    "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
    "strict": true, /* Enable all strict type-checking options. */
    "skipLibCheck": true, /* Skip type checking all .d.ts files. */
  },
  "include": [
    "./**/*.ts",
    "./**/*.d.ts"
  ]
}
  • 安装jquery 依赖
pnpm add jquery -S
  • 在根目录下新建index.ts文件和utils文件夹,在utils文件夹新建test.js文件
// utils/test.js

const num = 10

let position = {
  x: 100,
  y: 100
}

function add(x, y) {
  return x + y
}

function sayHi(name) {
  console.log(`Hi,${name}`);
}

const tools = {
  cookies: {
    value: 'cookies',
    get() {
      return this.value
    },
    set(value) {
      this.value = value
    }
  },
  fn() {
    console.log('这是fn函数');
  }
}

class Person {
  name;
  age;
  constructor(name, age) {
    this.name = name
    this.age = age
  }
}

export {num, position, add, sayHi, tools, Person}
// index.ts

import * as JQuery from 'jquery'
import {num, position, sayHi, add, tools, Person} from './utils/test.js'

console.log(add(position.x, num));

const name = 'tom'
sayHi(name)

console.log(tools.cookies.get());

const person = new Person('张三', 25)
console.log(person.name + person.age);

打开index.ts文件,发现报错找不到模块

为库代码编写声明文件

我们以jquery作为示范,在根目录下创建一个jquery.d.ts的文件,填写如下内容

// jquery.d.ts

declare module 'jquery' 

再次查看index.ts文件就能看到,关于jquery的报错就没了

为项目中封装的js代码编写声明文件

一般使用和js文件相同的名字来作为声明文件的文件名,我们以utils/test.js为例,在utils文件夹下新建test.d.ts来作为test.js的声明文件,填写如下内容,编写这类声明文件其实是有技巧的,一般是将js文件的内容全部复制到.d.ts文件中,然后删除逻辑处理的无用代码,再添加上类型即可

// utils/test.d.ts

declare const num: number

interface IPosition {
  x: number,
  y: number
}

declare let position:IPosition

declare function add(x:number, y:number):number

declare function sayHi(name:string): void

declare namespace tools {
  const cookies: {
    value: string
    get: () => string
    ste: (value: string) => void
  }
  function fn():void
}

declare class Person {
  name: string;
  age: number
  constructor(name: string, age: number)
}

export {num, position, add, sayHi, tools, Person}

再次查看index.ts文件就能看到,关于test.js的报错也没了,如果不生效可以重启编辑器