TS 知识扩展
模块化
JavaScript 有一个很长的处理模块化代码的历史,TypeScript 从 2012 年开始跟进,现在已经实现支持了很多格式。但是随着 时间流逝,社区和 JavaScript 规范已经使用为名为 ES Module 的格式,这也就是我们所知的 import/export 语法。
- ES 模块在 2015 年被添加到 JavaScript 规范中,到 2020 年,大部分的 web 浏览器和 JavaScript 运行环境都已经广泛支持。
- 所以在
TypeScript中最主要使用的模块化方案就是ES Module
;
ts
// math.ts
export function sum(num1: number, num2: number) {
return num1 + num2
}
ts
// type.ts
export interface IPerson {
name: string
age: number
}
export type IDType = number | string
ts
// index.ts
import { sum } from './utils/math'
import { type IPerson, type IDType } from './utils/type'
console.log(sum(123, 321)) // 444
const id: IDType = 123
console.log(id) // 123
const sj: IPerson = {
name: 'sj',
age: 19,
}
console.log(sj) // { name: 'sj', age: 19 }
非模块
我们需要先理解 TypeScript 认为什么是一个模块。
- JavaScript 规范声明任何
没有 export 的 JavaScript 文件都应该被认为是一个脚本,而非一个模块
。 - 在一个脚本文件中,
变量和类型会被声明在共享的全局作用域
,将多个输入文件合并成一个输出文件,或者在 HTML 使用多 个<script>
标签加载这些文件。
如果你有一个文件,现在没有任何 import 或者 export
,但是你希望它被作为模块处理,添加这行代码
:
ts
export {}
类型的查找
.d.ts 文件:
- 我们之前编写的 typescript 文件都是 .ts 文件,这些文件最终会输出 .js 文件,也是我们通常编写代码的地方;
- 还有另外一种文件 .d.ts 文件,它是用来做
类型的声明(declare)
,称之为类型声明(Type Declaration)
或者类型定义(Type Definition)
文件。 - 它仅仅用来做类型检测,告知 typescript 我们有哪些类型;
类型声明:
- 内置类型声明;
- 外部定义类型声明;
- 自己定义类型声明;
内置类型声明
内置类型声明是 typescript 自带的、帮助我们内置了 JavaScript 运行时的一些标准化 API 的声明文件;
- 包括比如 Function、String、Math、Date 等内置类型;
- 也包括运行环境中的 DOM API,比如 Window、Document 等;
TypeScript 使用模式命名这些声明文件 lib.[something].d.ts
。
内置类型声明通常在我们安装 typescript 的环境中会带有的;TypeScript 内置
我们可以通过 target 和 lib 来决定哪些内置类型声明是可以使用的:
- 例如,startsWith 字符串方法只能从称为 ECMAScript 6 的 JavaScript 版本开始使用
我们可以通过 target 的编译选项来配置:TypeScript 通过 lib 根据您的 target 设置更改默认包含的文件来帮助解决此问题。TypeScript lib
webpack 搭建
sh
# 初始化
npm init
# 安装 webpack
npm i webpack webpack-cli -D
# 安装 ts-loader
npm i ts-loader -D
# 安装 html-webpack-plugin
npm i html-webpack-plugin -D
# 安装本地服务
npm i webpack-dev-server -D
# 初始化 tsconfing.json
tsc --init
js
const path = require('path')
const HtmlWeabpckPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
entry: './src/index.ts',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js',
},
resolve: {
extensions: ['.ts', '.js', '.cjs', '.json'],
},
devServer: {},
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader',
},
{
test: /\.(png|jpe?g|svg|gif)$/,
type: 'asset/resource',
},
],
},
plugins: [
new HtmlWeabpckPlugin({
template: './index.html',
}),
],
}
json
// package.json
"scripts": {
"server": "webpack server",
"build": "webpack"
},
外部定义 – 第三方库
方式一:在自己库中进行类型声明(编写.d.ts 文件),比如 axios
方式二:通过社区的一个公有库 DefinitelyTyped 存放类型声明文件
- 该库的 GitHub 地址:https://github.com/DefinitelyTyped/DefinitelyTyped/
- 该库查找声明安装方式的地址:https://www.typescriptlang.org/dt/search?search=
- 比如我们安装 react 的类型声明: npm i @types/react --save-dev
外部定义 – 自定义声明
什么情况下需要自己来定义声明文件呢?
- 情况一:我们使用的
第三方库是一个纯的 JavaScript 库
,没有对应的声明文件;比如 lodash - 情况二:我们
给自己的代码中声明一些类型
,方便在其他地方直接进行使用;
declare 声明模块
ts
// 我们也可以声明模块,比如lodash模块默认不能使用的情况,可以自己来声明这个模块:
declare module 'lodash' {
export function join(args: any[]): any
}
声明模块的语法: declare module '模块名' {}
。
- 在
声明模块的内部
,我们可以通过 export 导出对应库的类、函数等
declare 声明文件
在某些情况下,我们也可以声明文件:
- 比如
在开发 vue 的过程中,默认是不识别我们的.vue 文件
的,那么我们就需要对其进行文件的声明
; - 比如
在开发中我们使用了 jpg 这类图片文件,默认 typescript 也是不支持的,也需要对其进行声明
;
ts
// type
declare module '*.jpg'
ts
// index
import sjImage from './images/01.jpg'
const imgEl = document.createElement('img')
imgEl.src = sjImage
document.body.append(imgEl)
declare 命名空间
ts
// type
declare namespace $ {
export function ajax(setting: any): any
}
ts
// index.ts
$.ajax({
url: 'https://api.thecatapi.com/v1/images/search',
success: function (res: any) {
console.log(res)
},
})
Axios 封装
- 创建 service 文件夹
- service 里面创建 config & modules & request 文件夹
- service 里面创建 index.ts 文件
ts
// config => index.ts
export const BASE_URL = 'http://codercba.com:8000'
export const TIMEOUT = 10000
ts
// request => type.ts
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
// 针对 AxiosRequestConfig 进行扩展
export interface SJInterceptors {
requestSuccessFn?: (config: AxiosRequestConfig) => any
requestFailureFn?: (err: any) => any
responseSuccessFn?: (res: AxiosResponse) => AxiosResponse
responseFailureFn?: (err: any) => any
}
export interface SJRequestConfig extends AxiosRequestConfig {
interceptors?: SJInterceptors
}
ts
// request => index.ts
import axios from 'axios'
import type { AxiosInstance } from 'axios'
import type { SJRequestConfig } from './type'
// 拦截器: 蒙版 loading | token | 修改配置等
class SJRequest {
instance: AxiosInstance
// requset 实例 => Axios 实例
constructor(config: SJRequestConfig) {
this.instance = axios.create(config)
// 每个 instance 实例都添加拦截器
this.instance.interceptors.request.use(
(config) => {
// loading/token
console.log('全局请求成功的拦截')
return config
},
(err) => {
console.log('全局请求失败的拦截')
return err
}
)
this.instance.interceptors.response.use(
(res) => {
console.log('全局响应成功的拦截')
return res.data
},
(err) => {
console.log('全局响应失败的拦截')
return err
}
)
// 针对特定的 SJRequest 实例添加拦截器
this.instance.interceptors.request.use(
config.interceptors?.requestSuccessFn,
config.interceptors?.requestFailureFn
)
this.instance.interceptors.response.use(
config.interceptors?.responseSuccessFn,
config.interceptors?.responseFailureFn
)
}
// 封装网络请求
request(config: SJRequestConfig) {
return this.instance.request(config)
}
get(config: SJRequestConfig) {
return this.request({ ...config, method: 'GET' })
}
post(config: SJRequestConfig) {
return this.request({ ...config, method: 'POST' })
}
delete(config: SJRequestConfig) {
return this.request({ ...config, method: 'DELETE' })
}
patch(config: SJRequestConfig) {
return this.request({ ...config, method: 'PATCH' })
}
}
export default SJRequest
ts
// index.ts
import { BASE_URL, TIMEOUT } from './config'
import SJRequest from './request'
const sjRequest = new SJRequest({
baseURL: BASE_URL,
timeout: TIMEOUT,
interceptors: {
requestSuccessFn: (config) => {
console.log('精细请求成功的拦截')
return config
},
requestFailureFn: (err) => {
console.log('精细请求失败的拦截')
return err
},
responseSuccessFn: (res) => {
console.log('精细响应成功的拦截')
return res
},
responseFailureFn: (err) => {
console.log('精细响应失败的拦截')
return err
},
},
})
export { sjRequest }
ts
// modules => home.ts
import sjRequest from '..'
// 发送网络请求
sjRequest
.request({
url: '/home/multidata',
})
.then((res) => {
console.log(res)
})