全局: 如何忽略ts检查
// 单行忽略
// @ts-ignore
// 忽略全文
// @ts-nocheck
// 检查全文
// @ts-check
全局: 如何设置类型声明
目标
- projec/types/xxx.d.ts 类型声明文件有效
{
"compilerOptions": {
"typeRoots": [
"./node_modules/@types/",
"./types" // 设置1
]
},
"include": [
"src/**/*.ts",
"src/**/*.vue", "types/**/*.d.ts"], // 设置2
}
number: ts 定义一个num: number 但是 num 默认值不想写数字 怎么破?
const num1: number | null = 1
const num2: number | undefined = 1
num?:number // 同上,用于interface的key
string: 限制取值范围
// 方式1 显示定义字符串范围
type sColor = "black" | "white" | "green"; // 取值限制范围
const s1: sColor = "black" // ok
const s2: sColor = "white" // ok
const s3: sColor = "green" // ok
const s4: sColor = "yellow" // error
// 方式2:通过枚举定义字符串范围
enum eColor {
black = 'black',
white = 'white',
green = 'green'
}
// 取值限制范围 同上面等价
type sColor = keyof typeof eColor;
const s1: sColor = 'black' // ok
const s2: sColor = "white" // ok
const s3: sColor = "green" // ok
const s4: sColor = "yellow" // error
// 优点: 枚举可以用于其他地方
if(data.background === eColor.black) // 判断
data[eColor.black] // 作为对象的key
enum: 定义枚举 && 使用枚举
// 定义eCheckStatus(ts类型): 枚举值,用于checkStatusMap对象的key名
export enum eCheckStatus {
toVerify = 0,
pass = 1,
notPass = 2,
}
// 定义iTag(ts类型): 接口,key是任意数字,value是嵌套接口
interface iTag {
[key:number]:{
label: string, // 页面显示文字内容
type?: string, // 页面显示文字颜色
}
}
// 使用ts类型: eCheckStatus(内部key名)+ iTag(整体结构), 在页面中展示需要
// 注意: 枚举 eCheckStatus 使用姿势是中括号
export const checkStatusMap:iTag = {
[eCheckStatus.toVerify]:{
label: '待审核',
type: ''
},
[eCheckStatus.pass]:{
label: '通过',
type: 'success'
},
[eCheckStatus.notPass]:{
label: '未通过',
type: 'info'
}
}
// 使用ts类型: eCheckStatus 在 js 逻辑中需要,用于添加判断
v-if="scope.row.checkStatus === eCheckStatus.toVerify"
Array: 可能是一维数组或二维数组
// 需求: 可能是一维数组或二维数组
type iArr = (string|string[])[]
const a1: iArr = ['1','2','3'] // 一维数组
const a2: iArr = [['1'],['2'],['3']] // 二维数组
Array: 数组里面是对象
const cells: Array<{ label: string; value: string }> = [
{
label: '费用',
value: '10,000.00'
}, {
label: '总计',
value: '10,000.00'
}, {
label: '余额',
value: '10,000.00'
}
]
interface: 如何继承和覆盖某些字段?
目标
- 继承A的大部分类型,但要调整个别类型
- 通过extends实现
interface A {
a: number;
b: number;
}
// Omit<A, 'a'> 等价于 剔除 a 后的接口 {b:number}
interface B extends Omit<A, 'a'> {
a: boolean;
c: string;
}
// 最终B的类型为 {a:boolean, b:number, c:string}
const b: B = {
a:true, // 在A的基础上调整
b:1, // 继承A的类型
c:'hi' // 在A的基础上新增
}
interface: key可能为任意字符串 && 嵌套对象
interface TagValue {
min: number,
max: number
}
interface Tag {
// 1. 对象嵌套对象 TagValue
// 2. 对象key propname: 可能为任意字符串或数字
[propname: string | number]: TagValue,
}
const t: Tag = {
"apple": { min: 3, max: 5 },
100: { min: 1, max: 100 },
}
interface: 定义多个key,必须和非必须
interface IBasicLayout {
loading: any; // 必须key
[key: string]: any; // 非必须key 可有可无
}
let data1: IBasicLayout = {
loading: false
}
let data2: IBasicLayout = {
loading: true,
userName: 'abc'
}
export {
print
}
interface: key名不确定
// 方式1: 使用interface
interface iMenu1 {
[key: string]: string;
};
const im1: iMenu1 = {M: '目录',C: '菜单',F: '按钮',}; // ok
const im2: iMenu1 = {age:100}; // error 因为 100是 number
// 方式2: 使用Record
type iMenu2 = Record<string, string>
const rm1: iMenu2 = {M: '目录',C: '菜单',F: '按钮',}; // ok
const rm2: iMenu2 = {age:100}; // error 因为 100是 number
泛型: 接口请求
目标
- 使用泛型T来抽象返回值类型
// 定义接口返回值类型 iRes
interface iRes {
id: string;
url: string;
width: number;
height: number;
}
// 发起请求函数getJson: 泛型用来定义返回值ts类型
// 第1个T是定义泛型,第2个T是使用泛型(用于返回类型)
async function getJson<T>(url: string): Promise<T> {
const response: Response = await fetch(url);
const json: Promise<T> = await response.json();
return json;
}
// 入口函数getData
async function getData(): Promise<void> {
try {
const url: string = 'https://xxx/search'
const json: iRes[] = await getJson<iRes[]>(url)
const data: iRes = json[0];
console.log('请求正常: data=', data)
} catch (error: Error | unknown) {
let message: string = error instanceof (Error) ? error.message : String(error);
console.error('发生错误: ', message)
}
}
window.onload = function () {
const button = document.querySelector('.button') as HTMLButtonElement;
button && button.addEventListener('click', getData, false);
}
// 测试
// 1. html中准备 <button class="button">按钮</button>
// 2. 必须服务器方式运行index.html才能请求成功,如 open with live server
// 结果:
// 请求正常: data= {
// "id": "c2r",
// "url": "https://xxx/images/c2r.jpg",
// "width": 500,
// "height": 374
// }
defineProps: 定义数组类型
interface List {
id: number,
content: string
}
defineProps<{
list: List[]
}>()
pit: vite.config.js Cannot find module ‘path’ ‘__dirname’
// 问题 vite.config.js 中 报错
import path from 'path';
// 类似其他问题 找不到 __dirname
path.resolve(__dirname, '../dist')
// 解决办法: 添加官方path声明,无需配置
yarn add -D @types/node
pit: Could not find a declaration file for module ‘vue-xxx‘
// 问题代码
import VueWechatTitle from 'vue-wechat-title'
// 解决办法1 cjs 代替 esm 导入
const VueWechatTitle = require('vue-wechat-title')
// ❌ ReferenceError: require is not defined 应该是 vue-wechat-title 不是cjs导出
// 解决办法2 修改ts配置 tsconfig.json ,运行导入js
{
"compilerOptions":{
"noImplicitAny": false,
"allowJs": true,
}
}
pit: Cannot find module ‘weixin-js-sdk’ or its corresponding type declarations.
// 问题代码
yarn add weixin-js-sdk // "weixin-js-sdk": "^1.6.0"
import wx from "weixin-js-sdk";
// 解决办法
// 方法1. 直接使用ignore忽略语法检测: 因为实际不影响代码正常运行的,打印wx对象,正常的 包含52个方法
// 方法2. 考虑换成 weixin-js-sdk-ts, 从github可以看到 js和ts的版本匹配,同样打印正常 并且包含52个方法
// 如何使用weixin-js-sdk-ts
// https://github.com/zhaoky/wx-jssdk-ts
// 1. 安装 2.引入
yarn remove weixin-js-sdk
yarn add weixin-js-sdk-ts // "weixin-js-sdk-ts": "^1.6.1"
import wx from "weixin-js-sdk-ts";
pit: Cannot find module ‘…/views/HomeView.vue’ or its corresponding type declarations.ts
// 问题 vue3 ts ts无法识别 vue 文件
// 同样的报错 Cannot find module './App.vue'
// 解决办法
// 在根路径 env.d.ts 或 src/types/env.d.ts 添加以下声明
// ❗文件生效的前提是 tsconfig.json 中配置了 "include": ["env.d.ts"],
declare module '*.vue' {
import { DefineComponent } from 'vue';
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>;
export default component;
}
pit: Cannot find module ‘@babel/types’ or its corresponding type declarations.
// 解决办法
// 1. yarn add @babel/types
// 2. 配置 ts.config.json中的 paths "@babel/types" 配置
// 3. yarn add --dev @types/babel__types // 这个没有做也可以
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@babel/types": ["node_modules/@babel/types"] // 新增
},
}
pit: Cannot find module ‘lodash-es’ or its corresponding type declarations.
# 原因: 未找到 lodash-es 的类型声明
解决办法
- yarn add lodash-es
- yarn add -D @types/lodash-es
- 在 tsconfig.json compilerOptions 中开启: "esModuleInterop": true
- 重启vscode
pit: document.querySelector 可能为空
// problem
function test2() {
// 报错: button可能为nul
const button = document.querySelector('button');
}
// solution
function test2() {
// 解决办法1. 使用断言 非常确定的类型
const button1 = document.querySelector('button') as HTMLButtonElement
// 解决办法2. 使用联合类型
const button2: HTMLButtonElement | null = document.querySelector('button');
}
pit: Property ‘resetFields’ does not exist on type 'Ref
背景:
- ts + vue3 + element plus
- 根据element plus 官方文档校验规则
// before
import type { FormInstance } from 'element-plus'
const menuRef = ref<FormInstance>()
function reset() {
form.value = initFormData();
if (!menuRef) return
menuRef.resetFields()
}
// after
import type { FormInstance } from 'element-plus'
const menuRef = ref<FormInstance>()
function reset() {
form.value = initFormData();
if (!menuRef) return
// ❗ref变量需要使用value
menuRef.value?.resetFields()
}
pit: Element implicitly has an ‘any’ type because expression of type ‘any’ can’t be used to index type
// before
const orderStatusMap = {
101: '待付款',
102: '用户取消',
901: '已完成',
};
<el-table-column align="center" label="订单状态">
<template #default="scope">
// ts报错: Element implicitly has an 'any' type because expression of type 'any' can't be used to index type
// 原因: orderStatusMap key 的类型不确定
<el-tag>{{ orderStatusMap[scope.row.status ]}}</el-tag>
</template>
</el-table-column>
// after
// 解决办法: 目前 orderStatusMap key 类型为string
type IFiledMap = {
[key: string]: string
}
const orderStatusMap: IFiledMap = {
101: '待付款',
102: '用户取消',
901: '已完成',
};
<el-table-column align="center" label="订单状态">
<template #default="scope">
<el-tag>{{ orderStatusMap[scope.row.status ]}}</el-tag>
</template>
</el-table-column>
pit: 没有ts版本的第三方库,提示 Could not find a declaration file for module
note-work-pit/front-ts/[ts]Could not find a declaration file for module.md
pit:Property ‘style’ does not exist on type ‘Element’.
// 问题代码
firstEle.style.flexGrow = 1
// 解决办法: 声明 firstEle 的 ts类型
const firstEle:HTMLElement = document.querySelector('.accordion-item')!
firstEle["style"].flexGrow = '1'
pit: Property ‘Vue’ does not exist on type ‘Window & typeof globalThis’.ts(2339)
// src/types/index.d.ts
export {};
declare global {
interface Window {
example: string;
}
}
pit: Property ‘offsetTop’ does not exist on type ‘Element’
// before
// 原因:Element 类型上没有 offsetTop
const element = document.getElementById('my-element') as Element;
const offsetTop = element.offsetTop;
// 修改后
// 解决办法: HTMLElement 类型上有 offsetTop,可以在类型上点进去看到
const element = document.getElementById('my-element') as HTMLElement;
const offsetTop = element.offsetTop;
pit: document.getElementById object is possibly ‘null’
// 问题代码
// 原因: ts告诉你 这个返回值可能为null
document.getElementById('app').scrollTop = 0;
// 解决办法
// 方法1. 很自信这个对象一定存在,可以用 !解决
document.getElementById('app')!.scrollTop = 0;
// 方法2. 不确定这个对象是否存在,则判断为null请求处理即可
const appEle = document.getElementById('app')
if(appEle){
appEle.scrollTop = 0;
}
pit: Cannot find type definition file for ‘element-plus/global’.
// 问题 找不到 element plus 组件的类型声明
// 原因 https://github.com/element-plus/element-plus/issues/4716
// 解决办法: 修改 ts.config.json 配置
"compilerOptions": {
// "types": ["element-plus/global"] // 删除
}
"include": ["node_modules/element-plus/global.d.ts"] // 新增
pit: Option ‘suppressImplicitAnyIndexErrors’ is deprecated and will stop functioning in TypeScript 5.5. Specify compilerOption ‘“ignoreDeprecations”: “5.0”’ to silence this error.
// 原因:和ts版本某些属性废弃有关
"compilerOptions": {
"suppressImplicitAnyIndexErrors":true, // 代码编译有问题 =》解决1.代码规范
"ignoreDeprecations": "5.0", // 解决办法2: 声明忽略5.5版本的废弃属性
}
pit ‘AbortSignal’ was also declared here.
# 报错
# node_modules/typescript/lib/lib.dom.d.ts:2071:13
# 2071 declare var AbortSignal: {
# ~~~~~~~~~~~
# 'AbortSignal' was also declared here.
# 解决办法
yarn add --dev @types/node@latest
pit element-plus Options Cannot find name ‘Options’.
问题:
node_modules/element-plus/es/components/select/index.d.ts:6468:24 - error TS2304: Cannot find name 'Options'.
6468 default: () => Options;
~~~~~~~
node_modules/element-plus/es/components/tree-v2/index.d.ts:99:511 - error TS2300: Duplicate identifier 'event'.
# 解决办法
官方issue: open状态,看起来未解决
https://github.com/element-plus/element-plus/issues/13177
https://github.com/element-plus/element-plus/discussions/10721 帖子里提到的例子 element-plus版本是 2.3.1
升级 element-plus 2.3.3 到 2.3.1或最新2.3.6 关注 yarn.lock
结果: 未解决