菜鸟笔记--TypeScript使用过程中的问题记录

前言

本人TS菜鸟一枚,在项目中,使用ts的过程中,踩过许多坑,有的问题记录下来了解决方案,有的疏漏了。现将记录下来的一些TS问题的解决方案给大家分享一下,希望大家能在工作中少踩点TS的坑,每天都能到点下班,带着愉快的心情回家。

30. interface和type的区别?

它们都可以描述一个对象或者函数,区别是:

  • type 可以声明基本类型别名,联合类型,元组等类型
  • type 可以使用 typeof 获取实例类型进行赋值
  • interface 能够声明合并
  • interface扩展类型语法是 extends,type扩展类型语法是 & (交叉)

29. setTimeout类型的定义

ts 复制代码
const timer:Ref<ReturnType<typeof setTimeout>> = ref(null);

28. tsconfig.json的path别名设置,有可能会被覆盖。造成所有别名路径都报警告。模块不存在。不要在子应用中的tsconfig.json中重新定义path字段。

27.includes方法参数报内容不能赋值给never类型,解决方法:将前面的值转换成数组类型

ts 复制代码
  selectedRowKeys.value?.includes(record?.[props.rowKey] as [])

26. 动态方法名不会触发ts告警的正确写法

ts 复制代码
// 错误写法
// status 0启用 1禁用
let apiName = status === 0 ? 'disabledProtocol' : 'enableProtocol';
(agreementApi[apiName as any] as any)({ protocolId })
// ...

// 正确写法
agreementApi[status === 0 ? 'disabledProtocol' : 'enableProtocol']({ protocolId })
// ...

25. 给一个对象初始化赋值为{}或者一个数组初始化赋值为[ ]引起的ts告警如何解决?

参考如下案例:

ts 复制代码
interface Foo {
  bar: number;
  bas: string;
}

let foo = {} as Foo;
foo.bar = 123;
foo.bas = 'Hello World';

24. props某个属性有多种类型定义方式

ts 复制代码
import { defineComponent } from 'vue'
import type { PropType } from 'vue'

// 定义具体的函数签名有助于更好的类型推断
type ApiFunction = () => number;

export default defineComponent({
  props: {
    apiAction: [String, Function] as PropType<string | ApiFunction>,
    default: ''
  }
})

23. 常用的几种操作

  • 限定方法参数名类型
ts 复制代码
function get<T extends object, K extends keyof T>(o: T, name: K): T[K] { return o[name] }
  • 取出item[]中的item类型
ts 复制代码
type IItem=ActivityListResponse['data'][0]
  • 覆盖原有类型
ts 复制代码
type Modify<T, R> = Omit<T, keyof R> & R;

22. ts中 object VS Object VS {}的区别

这三者的区别是:

  1. 原型上属性方法重写表现不一致 Object不能重写原型上的方法, {}和object可以。
  2. object类型值表示⾮原始类型,Object,{}类型值可以为原始类型 number|string:boolean;

看下面的实验:

  • Object
ts 复制代码
let obj: Object; // 或者 let obj = new Object();
obj = "hell oworld";
obj = 1;
obj = true;
obj = undefined; //Error:Type 'undefined' is not assignable to type 'Object'.
obj = {
      // Type 'number' is not assignable to type 'string'
    toString() {
        return 123;
    }
}
  • {}
ts 复制代码
let obj: {}; 
obj = undefined; //Error:Type 'undefined' is not assignable to type '{}'.
obj = 'a';
obj = {
    a : "hell oworld",
    b : 1,
    c : true,
    toString() {
        return 123;
    }
}
console.log(obj) 
/*
{
  "a": "hell oworld",
  "b": 1,
  "c": true
} 
*/
console.log(obj.toString()) // 123;
  • object
ts 复制代码
let obj: object; 
obj = 1; // Error:Type 'number' is not assignable to type 'object'.
obj = true; // Error: Type 'boolean' is not assignable to type 'object'.
obj = 'a'; // Error: Type 'string' is not assignable to type 'object'.
obj = undefined; //Error:Type 'undefined' is not assignable to type 'object'.
obj = {
    a : "hell oworld",
    b : 1,
    c : true,
}

obj = {
    toString() {
        return 123;
    }
}
console.log(obj.toString()) // 123

综上所述,object才是我们想要的定义一个空对象所需的类型。

21. 索引报错是个烦恼而不是助力

对象的key使用变量索引时,VSCode会报标示红色的波浪线,报:元素隐式具有 "any" 类型,因为类型为 "string" 的表达式不能用于索引类型 "{}"。\在类型 "{}" 上找不到具有类型为 "string" 的参数的索引签名。ts(7053)的解决方法。

ts 复制代码
const person = {
    name: '张三',
    age: 10
};

function getValue(arg: string) {
    return person[arg];
}

 TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ isMobile: string; isUnion: string; isTelcom: string; no: s
tring; }'

解决方法:在tsconfig.json中配置

json 复制代码
{
  "compilerOptions": {
    "suppressImplicitAnyIndexErrors":true,
  },
}

20. 组件已引入并可以使用,ts却提示找不到组件模块,如何解决?

加一条类型声明即可

ts 复制代码
declare module '@sensetime/razor';

19. 交叉|联合类型

交叉类型--定义的变量中要有多种类型中的所有属性,联合类型--定义的变量要符合多种类型的某一种。

交叉类型 有同名属性时,类型要同时满足多种类型中的同名字段类型。比如leftType中id的类型是0|1,RightType中id的类型是number,那么交叉类型中的id,就要满足0|1,又要满足number

联合类型 有同名属性时,类型要同时满足多种类型中某一种类型的同名字段类型。比如leftType中left的类型是'0'|'1',RightType中left的类型是string那么联合类型中的left,要么满足'0'|'1',要么满足string

ts 复制代码
interface LeftType {
  id: 0 | 1;
  left: '0' | '1';
}

interface RightType {
  id: number;
  left: string;
}

type IntersectionType = LeftType & RightType;
const t: IntersectionType = {
  id: 0,
  left: '1',
};

type UnionType = LeftType | RightType;
const t: IntersectionType = {
  id: 4,
  left: '4',
};

18. react-router history方法不存在的解决方法

bash 复制代码
# 注意node的版本必须大于v12
yarn add @types/react-router @types/history -D

方法一:

ts 复制代码
import * as H from 'history';
interface IProps {
  history: H.History;
}

方法二:

ts 复制代码
import { RouteComponentProps } from 'react-router';

// 声明全局变量
declare global {
  type history = RouteComponentProps['history'];
}

17. react ref类型定义方法

html元素类型定义

js 复制代码
class TestApp extends React.Component<AppProps, AppState> {
    private stepInput: React.RefObject<HTMLInputElement>;
    constructor(props) {
        super(props);
        this.stepInput = React.createRef();
    }
    render() {
        return <input type="text" ref={this.stepInput} />;
    }
}

组件类型定义

ts 复制代码
import React, { createRef, RefObject } from 'react';

import { RankArea } from '@components/marketAnalyse';

// 浏览产品+开售提醒+前往办理 客户数排名组件引用
private rankAreaRef: RefObject<RankArea> = createRef();

 <RankArea ref={this.rankAreaRef} data={responseMap} />

16.tsconfig.json配置字段含义

json 复制代码
{
  "compilerOptions": {

    /* 基本选项 */
    "target": "es5",                       // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
    "module": "commonjs",                  // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
    "lib": [],                             // 指定要包含在编译中的库文件
    "allowJs": true,                       // 允许编译 javascript 文件
    "checkJs": true,                       // 报告 javascript 文件中的错误
    "jsx": "preserve",                     // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
    "declaration": true,                   // 生成相应的 '.d.ts' 文件
    "sourceMap": true,                     // 生成相应的 '.map' 文件
    "outFile": "./",                       // 将输出文件合并为一个文件
    "outDir": "./",                        // 指定输出目录
    "rootDir": "./",                       // 用来控制输出目录结构 --outDir.
    "removeComments": true,                // 删除编译后的所有的注释
    "noEmit": true,                        // 不生成输出文件
    "importHelpers": true,                 // 从 tslib 导入辅助工具函数
    "isolatedModules": true,               // 将每个文件作为单独的模块 (与 'ts.transpileModule' 类似).

    /* 严格的类型检查选项 */
    "strict": true,                        // 启用所有严格类型检查选项
    "noImplicitAny": true,                 // 在表达式和声明上有隐含的 any类型时报错
    "strictNullChecks": true,              // 启用严格的 null 检查
    "noImplicitThis": true,                // 当 this 表达式值为 any 类型的时候,生成一个错误
    "alwaysStrict": true,                  // 以严格模式检查每个模块,并在每个文件里加入 'use strict'

    /* 额外的检查 */
    "noUnusedLocals": true,                // 有未使用的变量时,抛出错误
    "noUnusedParameters": true,            // 有未使用的参数时,抛出错误
    "noImplicitReturns": true,             // 并不是所有函数里的代码都有返回值时,抛出错误
    "noFallthroughCasesInSwitch": true,    // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)

    /* 模块解析选项 */
    "moduleResolution": "node",            // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
    "baseUrl": "./",                       // 用于解析非相对模块名称的基目录
    "paths": {},                           // 模块名到基于 baseUrl 的路径映射的列表
    "rootDirs": [],                        // 根文件夹列表,其组合内容表示项目运行时的结构内容
    "typeRoots": [],                       // 包含类型声明的文件列表
    "types": [],                           // 需要包含的类型声明文件名列表
    "allowSyntheticDefaultImports": true,  // 允许从没有设置默认导出的模块中默认导入。

    /* Source Map Options */
    "sourceRoot": "./",                    // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
    "mapRoot": "./",                       // 指定调试器应该找到映射文件而不是生成文件的位置
    "inlineSourceMap": true,               // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
    "inlineSources": true,                 // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性

    /* 其他选项 */
    "experimentalDecorators": true,        // 启用装饰器
    "emitDecoratorMetadata": true          // 为装饰器提供元数据的支持
  }
}
  • typeRoots 和 types 区别

typeRoots 和 types 这两个选项默认只对通过 npm 安装的声明模块有效,用户自定义的类型声明文件与它们没有任何关系。

默认的,所有位于 node_modules/@types 路径下的模块都会引入到编译器。相当于如下配置

json 复制代码
{
   "typeRoots" : ["node_modules/@types"]
}

具体来说是,./node_modules/@types 、../node_modules/@types、../../node_modules/@types 等等。

如果不希望自动引入 typeRoots 指定路径下的所有声明模块,那可以使用 types 指定自动引入哪些模块。

json 复制代码
{
   "types": ["lodash-es"],
}

禁止自动引入的话,配置为

json 复制代码
{
    "types":[]   
}

自定义声明文件的引入方法:

15. ts中vue文件不被识别

解决方案: 创建一个vue.d.ts的文件

ts 复制代码
  declare module "*.vue" {
    import Vue from 'vue'
    export default Vue
  }

14.类型"ImportMeta"上不存在属性"env"。

解决方法: 在typings下新建一个env.d.ts文件

ts 复制代码
interface ImportMeta {
  env: Record<string, unknown>
}

13. process.env报未定义的解决方法

bash 复制代码
yarn add @types/node

在项目下的tsconfig.json中添加如下配置

json 复制代码
{
  "compilerOptions": {
     // ...
    "typeRoots": ["node_modules/@types"], // 要包含的类型声明文件名列表
  }
}

12. 给div增加自定义属性的方法

可以扩展React命名空间,自定义一个react.d.ts文件,消除div上书写name属性报错的问题

ts 复制代码
declare module 'react' {
  interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
    // 扩展 React's HTMLAttributes
    name?: string;
  }
}

11. Class Component this上不存在xxx静态属性的声明方法的解决方法

ts 复制代码
class Foo extends React.Component{
  share:string[]='hello'
}

10. 不能将xxx类型赋值给xxx类型的解决方法:用as 断言

ts 复制代码
interface ICustomShareProps {
  current:unkown;
  [key: string]?: unknown;
}

const customShareRef = useRef<ICustomShareProps>({});

<CustomShare
   ref={(el) => (customShareRef.current = el as ICustomShareProps)}
   sensorsEvent={props.sensorsEvent}
   shareObj={shareObj}
   history={props.history}
   hideSomeShareOption={true}
/>

9.不能将类型void分配给类型ReactElement<any,any> | null,的解决方法:

修改react函数组件返回值

js 复制代码
const App: React.FC<any> = (): ReactElement<any, any> | null => {
  // ... 
}

8. 对象可能为null, 如何解决?

解决方法:

  1. 用可选链 ?.
  2. 在后面加个!,非常肯定地告诉ts编译器,对象一定存在
ts 复制代码
// 改法1
document.getElementById('share-box-bg')?.addEventListener
// 改法2
document.getElementById('share-box-bg')!.addEventListener

7. 如何在Typescript中定义Promise的返回值类型?

  1. 方法一 通过 Promise 的构造函数,声明返回值的泛型类型
ts 复制代码
const foo = new Promise<{code:number;msg:string}>((reslove, reject) => {
    reslove({code:0,msg:'ok'});
})
  1. 方法二 定义reslove的类型
ts 复制代码
const foo = new Promise((reslove:(value:{code:number;msg:string})=>void, reject) => {
    reslove({code:0,msg:'ok'});
})

6. 找不到名称wx,WeixinJSBridge和Document上不存在属性attachEvent的解决方法

在./src/typings/global.d.ts中定义如下内容:

ts 复制代码
// 声明全局变量
declare global {
  interface Window {
    $log: any;
    $error: any;
  }

  interface Document {
    attachEvent(event: string, listener: EventListener): boolean;
    detachEvent(event: string, listener: EventListener): void;
  }

  namespace WeixinJSBridge {
    function call(apiName: string): void;
  }

  namespace wx {
    function error(config: unknown): void;
    function config(config: unknown): void;
    function checkJsApi(config: unknown): void;
    function ready(config: unknown): void;
    function agentConfig(config: unknown): void;
    function onMenuShareTimeline(config: unknown): void;
    function onMenuShareAppMessage(config: unknown): void;
    function updateAppMessageShareData(config: unknown): void;
    function updateTimelineShareData(config: unknown): void;
    function invoke(apiName: string, config: object, cb: unknown): void;
    function hideMenuItems(config: unknown): void;
    function showMenuItems(config: unknown): void;
    function hideOptionMenu(): void;
    function getLocation(config: unknown): void;
  }
}

// esModule要求必须有导出项,全局声明实际上不必导出,这样写是为了避免编译器提示警告
export {};

然后在tsconfig.json中引入全局类型声明

ts 复制代码
{
    "include": ["./src/typings"]
}

5.全局类型声明无法通过eslint校验的处理方法,添加到globals声明中去。

js 复制代码
module.exports = {
  // ...
  globals: {
     "wx": "readonly",
     "WeixinJSBridge": "readonly"
  },
}

4. e.target.value不存在,解决方案

ts 复制代码
const foo=(e:ChangeEvent<HTMLInputElement>)=>{ console.log(e.target.value)}

其它事件类型还有这些:

ts 复制代码
AnimationEvent
ChangeEvent
ClipboardEvent
CompositionEvent
DragEvent
FormEvent
KeyboardEvent
MouseEvent
PointerEvent
TouchEvent
TransitionEvent
WheelEvent

3.禁用ts类型检查

有些ts报错处理起来既繁琐,意义也不大,这时候就可以考虑屏蔽一些ts错误

ts 复制代码
/*===忽略整个文件===*/
// @ts-nocheck
/*===忽略当行===*/
// @ts-ignore 

2.枚举定义,使用场景-状态值

ts 复制代码
// 枚举状态
enum TaskStatus {
  Undo = 0,
  Done = 1,
}
 
if(status === TaskStatus.Undo){
 // do something
}

1.取出接口类型的keys和value类型值

ts 复制代码
interface tabs {
  待处理: 1;
  已处理: 2;
}

// tab类型Label
type TTabLabel = keyof tabs;
type ValueOf<T> = T[keyof T];
相关推荐
golitter.4 分钟前
Ajax和axios简单用法
前端·ajax·okhttp
YUELEI1188 分钟前
TypeScript 封装 Axios 1.7.7
typescript·axios
雷特IT24 分钟前
Uncaught TypeError: 0 is not a function的解决方法
前端·javascript
长路 ㅤ   1 小时前
vite学习教程02、vite+vue2配置环境变量
前端·vite·环境变量·跨环境配置
亚里士多没有德7751 小时前
强制删除了windows自带的edge浏览器,重装不了怎么办【已解决】
前端·edge
micro2010141 小时前
Microsoft Edge 离线安装包制作或获取方法和下载地址分享
前端·edge
.生产的驴1 小时前
Electron Vue框架环境搭建 Vue3环境搭建
java·前端·vue.js·spring boot·后端·electron·ecmascript
awonw1 小时前
[前端][easyui]easyui select 默认值
前端·javascript·easyui
九圣残炎1 小时前
【Vue】vue-admin-template项目搭建
前端·vue.js·arcgis
柏箱2 小时前
使用JavaScript写一个网页端的四则运算器
前端·javascript·css