Skip to content

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 存放类型声明文件

外部定义 – 自定义声明

什么情况下需要自己来定义声明文件呢?

  • 情况一:我们使用的第三方库是一个纯的 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 封装

  1. 创建 service 文件夹
  2. service 里面创建 config & modules & request 文件夹
  3. 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)
	})