菜鸟笔记--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];
相关推荐
恋猫de小郭1 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端