TypeScript
安装和编译
npm instaill -g typescript
tsc -v
查看安装tsc hello.ts
编译tsc
编译当前目录下所有ts文件,可配置tsconfig.json => outDir输出目录,否则会在所有对应ts的路径下生成jstsc --watch
自动监听变化并编译
语法
类型检测
- 类型检测
v: t
- 联合类型
v: t1 | t2
表示可以是t1或者t2 - 交叉类型
v: t1 & t2
表示t1和t2都要满足,若t1、t2有字段类型冲突,该字段为never
- 类型断言,当编译器根据类型检测判断结果类型可能多个,跟理想类型不符,就容易报错。但是开发者已人为断定结果的类型,知道自己在干嘛,有两种写法:
<t>v
(v as t)
- 参数或属性必定有值断言:
v!: t
- 类型推断, ts在变量定义时若未声明类型检测,会自动推断出一个类型(未赋值为
any
),当类型变化会报错
像new Boolean()这样的内置对象创建出来的值是包装类型,不属于基本类型boolean
基础类型
boolean
、number
、string
、undefined
、null
;undefined
和null
是所有类型的子类型,可以赋值给其他类型 ^nsArray
;数组内所有元素类型检测:v: t[]
或v: Array<t>
(泛型)symbol
enum
枚举类型,支持基于数字和字符串的枚举:成员名为汉字可能值为undefined
1 | enum Direction { |
1 | //加const修饰成为常量枚举 |
还有异构枚举:数字和字符串的混合
注意 枚举类型的值并不能用变量动态获取,只能传定值。动态获取应该使用 对象 + 索引签名
any
允许对此类型的值做任何操作无需任何检查,允许所有更改unknown
与any
一样无需任何检查;但unknown
类型变量只能赋值给unknown
和any
类型;由于类型未知,除赋值外禁止任何更改tuple
元组类型 与数组类似,但用于有限数量的未命名属性的类型,必须提供每个属性的值。各个元素类型可不同,可通过push
等方法新增未知类型元素1
2
3let v: [string, number]
v = ['2', 2]
//访问方式与数组一致- 元组和数组类型都可以通过数字索引访问其中一项的类型。还可以通过
[number]
获取到对应的联合类型(数组只有一项类型)
1
2
3
4
5type arr = string[][1] //string
type tuple = [number, string][1] //string
type arr = string[][number] //string
type tuple = [number, string][number] //number | string- 元组和数组类型都可以通过数字索引访问其中一项的类型。还可以通过
void
表示没有任何类型,与any相反;严格模式下只能为undefined
1
2
3
4
5// 声明函数返回值为void
//定义函数的()后跟`: t`声明返回值类型
function warnUser(): void {
console.log("This is my warning message");
}object
、Object
、和{}
object
表示非原始类型Object
所有 Object 类的实例的类型,由以下两个接口定义:Object 接口定义了 Object.prototype 原型对象上的属性;
1
2
3
4
5
6
7
8
9interface Object {
constructor: Function;
toString(): string;
toLocaleString(): string;
valueOf(): Object;
hasOwnProperty(v: PropertyKey): boolean;
isPrototypeOf(v: Object): boolean;
propertyIsEnumerable(v: PropertyKey): boolean;
}ObjectConstructor 接口定义了 Object 类的属性
1
2
3
4
5
6
7
8
9
10
11interface ObjectConstructor {
/** Invocation via `new` */
new(value?: any): Object;
/** Invocation via function calls */
(value?: any): any;
readonly prototype: Object;
getPrototypeOf(o: any): any;
// ···
}
declare var Object: ObjectConstructor;
{}
一个空对象,访问其属性会报错,但仍可使用Object接口上的所有方法
never
永不存在值的类型: 总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值。可以赋值给任何类型^ns
Freshness: 对于对象类型,若对字面量进行类型检测,不允许已定义类型外的属性存在。如果不使用字面量而事先定义,则允许
其他类型
除了基本类型string、number
等,还有包装类型String, Number
。通过new 内置对象
创建出来的都是包装类型
HTMLElement
dom元素NodeList
节点列表,含有多个dom元素DocumentFragment
文档片段MouseEvent
鼠标事件参数,如click事件KeyboardEvent
键盘事件参数
typeof
包装类型都返回object,不准确
interface
定义对象、类的属性及其类型,函数的参数和返回值。
接口为空对象时无限制,只要规定了一个属性,其他属性及其类型都要完全对应,除非定义可选类型:
v?: t
规定属性只读,初始定义之后不能修改该属性:
readonly v: t
函数调用签名:
1
2
3
4
5
6
7
8
9//()内定义函数参数的类型,tr为返回值类型
//函数的参数名不需要与接口里定义的名字相匹配,但类型顺序必须对应
interface IP {
(v1: t1, v2: t2): tr
}
//使用:
const fn: IP = function (v1: t1, v2: t2):tr {
...
}索引签名,对数组、对象索引相关的约束(数组索引
number
,对象索引string
)1
2
3
4interface ND {
//可以加readonly防止给索引赋值: myArray[2] = "Mallory"
readonly [index: number]: string
}
infer类型分发
infer X
,X
将作为类型推断的结果并可对其操作
1 | // 推导泛型参数 |
keyof索引类型查询
keyof
关键字查询索引类型, 结果为该类型上所有共有属性(public)key的联合
如果T只是一个类型,
keyof T
产生T所有属性名称字符串的联合类型:1
2
3
4
5interface T {
name: string,
age: number
}
type KF = keyof T //name | age如果T是一个联合类型,
keyof T
产生联合类型的每一项的集合,可再[]通过关键字in
获取每一项的key:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//将类型T索引属性标记为可选
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface M {
name: string,
age: number
}
//报错: 'age' is missing
const a: M = {
name: "sss"
}
//正确
const a: Partial<M> = {
name: "sss"
}如果T是带有签名索引的类型:
1
2
3
4
5
6
7
8
9
10//索引为string,则keyof T为 string | number
interface T {
[key: string]: string
}
type Map = keyof T //string | number
//索引为number,则keyof T为 number
interface T {
[key: number]: string
}
type Map = keyof T //number
类型别名type
和interface作用基本相同,2.7版本后从有。用interface难描述的类型用type更方便。
语法类似声明变量,用type
关键字定义
interface支持与同名的class或interface自动聚合,而type不能重名
type支持复杂的类型操作,赋值等
类
ts对ES6的class进了的规范
属性注释。构造器外声明实例属性(es6)时可以进行类型注解,若类型注解时未赋初始值(用
=
),该class的构造函数必须保证该class的实例有对应的属性接口的约束
interface
+implements
:1
2
3
4
5
6interface IP {
say(): t
}
//implements用接口来约束类
//该类必须要有say()这个方法且返回值类型为t
class 类 implements IP {...}若希望被多个接口约束, 可以:
implements
时接口间可用逗号 ‘,’ 隔开使用
extends
创建子接口继承多个接口的约束, 接口间也用逗号 ‘,’ 隔开:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//接口继承
interface Child extends P1,P2 {}
//extends前面的参数为(泛型+联合类型)时则会分解(依次遍历所有的子类型进行条件判断)联合类型进行判断。然后将最终的结果组成新的联合类型:
//type A2 = 2
type A2 = 'x' | 'y' extends 'x' ? 1 : 2;
//type A3 = 1 | 2
type P<T> = T extends 'x' ? 1 : 2;
type A3 = P<'x' | 'y'>
//如果不想被分解(分发),可以通过简单的元组类型包裹:
type P<T> = [T] extends ['x'] ? 1 : 2;
//type A4 = 2;
type A4 = P<'x' | 'y'>
构造器函数外未用
static
的都是原型上的属性方法
类成员修饰符:
已经被接口限制的类成员无法使用修饰符, 只能再定义成员规定新成员的访问方式
public
,默认,允许任意访问private
, 私有,只能在当前类内部访问protected
, 受保护的, 只能在当前类或子类内部访问readonly
,只读,只有构造函数内或赋初始值时可修改
构造函数的参数也可以使用成员修饰符,修饰后会成为属性成员
存取器
get
、set
与es6用法基本一致,只是增加类型注解。注意每个getter和setter也不能重名
抽象类, 作为其它派生类的基类使用,不能被实例化
abstract
关键字定义抽象类及其内部的抽象方法,抽象方法必须在抽象类的子类中实现抽象类中还可以定义实例方法等一般的类成员,但除了实例方法外一般都不在抽象类中定义
抽象类源自多态思想: 父类定义方法不实现,交给子类进行不同的表现。
多态属于继承
类可以作为类型检测使用,也可作为泛型的参数
函数
函数的约束:
一般写法:
1
function fun(v1:t1, v2:t2):tr {...}
完整写法:
1
2//这里`=`两边是两种约束方式,可以只留一种
const fun:(v1:t1, v2:t2)=>tr = function fun(v1:t1, v2:t2):tr {...}函数的参数可以使用
?
成为可选参数:v?:t
。可选参数要写在普通参数的后面剩余参数: 与es6相同,
...v
得到函数剩余的参数数组,可使用数组类型检测函数重载, 当同一个函数,需要规定不同参数类型数量导致不同返回值,仅用联合类型
|
无法准确判断,需要在定义函数前:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//参数都是string返回string ,参数都是number返回number
function f(a:string, b:string):string
function f(a:number, b:number):number
//再定义函数
//当a,b类型不等时未返回值,要加void类型
function f (a:string|number, b:string|number):string|number|void {
interface E {
[key:string]: string|number
}
const empty:E = {
string: '',
number: 0
}
const ta:string = typeof a
if (ta === typeof b && a === 'string') return a + b
if (ta === typeof b && a === 'number') return a + b
}
泛型
使函数、接口、类定义的约束可复用,并支持不特定的数据类型。虽然可以使用any
类型,但无法准确报错和语法提示
函数泛型:
1
2
3
4
5
6
7//函数名后跟<T>,T代表使用时才获知的类型,T可以是其他字符
//定义和确定时多个泛型用`,`分割
function f<T, A>(a:T, b:A):[T, A] {
return [a, b]
}
//使用时函数名后跟<t>确定类型
console.log(f<number, string>(121, '22'))泛型接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27//UserCRUD的泛型接口
interface UserOpe<T> {
users: Array<T> //存放所有用户信息的数组
addUser: (u: T) => void //添加用户信息到users
findUser: (id: number) => T | void //查找用户
}
//创建一个用户的类,也作为泛型接口UserCRUD的确认类型
class User {
id: number
name: string
age: number
constructor(id: number, name: string, age: number) {
this.id = id
this.name = name
this.age = age
}
}
//将User作为泛型的确定类型
class UserCRUD implements UserOpe<User> {
users: User[] = []
addUser(user: User) {
this.users.push(user)
}
findUser(id: number) {
return this.users.find(ele => id === ele.id)
}
}泛型类:
1
2
3
4
5
6
7
8
9
10class Man<T> {
a: T
log(x: T): T {
return x
}
constructor(a: T) {
this.a = a
}
}
const m = new Man<number>(1)泛型约束
extends
,泛型定义时不确定数据类型,例如不清楚是否存在如length
方法。需要定义接口约束该泛型的数据必须要满足次条件(有length
属性):<T extends 接口>
1
2
3
4
5
6
7interface IL {
length: number
}
function fun<T extends IL> (x: T) {
return x.length
}
使用时不一定要手动确定类型,类型推断会自动推断确定类型
模块
内部模块(命名空间)
namespace
关键字定义,组织代码,避免命名冲突- 命名空间内容外部默认不可访问,需要:
- 用
export
向外暴露 - 外部通过
namespaceA.xx
访问
- 用
外部模块
同es6相同,以文件为一个模块
装饰器decorators
源自es7的装饰器,一种特殊类型的声明,能附加到类的声明、方法、属性或方法参数前。运行时会当作函数调用,修改扩展类的行为
写法
需要先定义好装饰函数,再使用
target是第一个参数,对静态成员是构造函数,对实例成员是类的原型对象
注意:类构造器外部的未使用static的属性会成为实例属性,而构造器外部未使用static的对象都保存在原型上
普通装饰器(无法传参)
1
2
3
4
5
6//使用
@fn
//定义
function fn(target) {
...
}装饰器工厂(可传参args)
1
2
3
4
5
6
7
8//使用
@fn
//定义
function fn(args) {
return function(target) {
...
}
}
种类
- 类装饰器,只有第一个参数target。如果最终返回类(不能有constructor)会重载所装饰的类,替换原有的构造函数
- 属性装饰器,第二个参数是装饰的属性key,第三个参数是属性描述(仅在以类原型上的成员为目标(例如根据访问器定义的方法或属性)时才可用。常规属性的装饰器仅需要一个目标和一个键, 可将第三个设置为可选参数避免报错)
- 方法装饰器,第二个参数是装饰的方法名,第三个参数是方法描述。修改第三个参数的
value
可重载此方法 - 方法参数装饰器(为类原型添加数据)。target是实例成员,第二个参数是方法名,第三个参数是参数索引
优先级: 属性方法 => 方法参数 => 类 (属性和方法是按写的顺序从上往下)
多个相同位置装饰器优先级从后向前,后面的先执行
由于decorators是实验性的,需要在tsconfig.json=>compilerOptions中设置
experimentalDecorators
为true
消除警告
声明文件
ts对于第三方库没有语法提示,如jq。常用库都有声明文件,否则可以手动下载: npm i -D @types/xxx
还可以自定义以.d.ts
结尾的文件,在内部用declare
声明语法提示,ts会自动检测到这些文件来提示。有export关键字会被认为是模块,没有export的声明文件内的类型会作用到全局
1 | declare var a: (x: number) => number |
一般第三方库也有准备对应的声明文件,额外下载即可
注意一般ts文件中使用import方式导入的模块才拥有类型注解。像node中使用require方式引入,虽然安装了@types/node不会报错,但会丢失类型,引入的都是any。
工具类型
typescript内置了很多工具类型可直接使用,方便操作,常用如下:
Partial<T>
将类型T所有属性标记为可选属性1
2
3type Partial<T> = {
[P in keyof T]?: T[P];
}Required<T>
将类型T所有属性都标为必选1
2
3
4type Required<T> = {
//-?表示移除?
[P in keyof T]-?: T[P];
};Readonly<T>
将类型T所有属性标为只读1
2
3type Readonly<T> = {
readonly [K in keyof T]: T[K]
}Pick<T, K>
从类型T中挑选出K指定的某个或某些属性1
2
3
4
5
6
7
8
9
10
11
12
13
14type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
interface Person {
name: string,
age: number,
gender: number
}
type otherPerson = Pick<Person, 'name' | 'age'>;
//otherPerson = {
// name: string;
// age: number;
//}Record<对象key或keyType, valueType>
标记对象属性key或value类型。索引签名的key只能是number、string、symbol或模板字面量类型,不能是字面量或泛型,此时可以用
Record
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//keyof any = string | number | symbol(任何类型的索引key只能是这三种)
type Record<K extends keyof any, T> = {
[L in K]: T
}
// 设置所有key和value的类型
const a: Record<string, number> = {
age: 18,
tel: 110
}
// 设置指定key的value的类型,必须对每个属性都指定
const a: Record<"age" | "tel", number> = {
age: 18,
tel: 110
}Exclude<T, k>
去掉T中与K有关类型1
2
3
4type Exclude<T, K> = T extends K ? never : T
//string
type obj = Exclude<string | number, number>Extract<T, K>
提取T中与K有关类型1
2
3
4type Extract<T, K> = T extends K ? T : never
//number
type obj = Extract<string | number, number>Omit<T, K>
忽略类型T中与K有关的属性1
2
3
4type Omit<T, K extends any> = Pick<T, Exclude<keyof T, K>>
//{age: number}
type Foo = Omit<{name: string, age: number}, 'name'>NonNullable<T>
去除类型T中null
和undefined
1
2
3
4type No<T> = T extends null | undefined ? never : T
//number | string
type a = No<number | null | string | undefined>ReturnType<T>
得到类型T(函数) 返回结果的类型1
2
3
4type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
// type t = string | number
type t = ReturnType<(name: string) => string | number>Parameters<T>
以元组方式获得类型T(函数)的入参类型1
2
3
4type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never
// type t = [string] | [number]
type t = Parameters<((name: string) => any) | ((age: number) => any)>InstanceType<T>
得到类型T(构造函数)返回结果的类型1
2
3
4type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any
//type t = {name: string, age: number}
type t = InstanceType<new (name: string) => {name: string, age: number}>ConstructorParameters<T>
以元组方式获得类型T(构造函数)的入参类型1
2
3
4type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never
//type t = [string] | [number]
type t = ConstructorParameters<(new (name: string) => any) | (new (age: number) => any)>;
Vue中使用TS
在vue2中一般使用TS,一般用class-style方式写组件,方便装饰器的使用
常用装饰器
从vue-property-decorator导入
Component
可定义组件的选项,跟组件class
内的属性方法都成为组件的选项Prop
修饰的属性将从父元素传递来的同名prop
接收,传入options可对其约束Emit
修饰的方法将实现可以emit
的传入名称的自定义方法
第三方库
vue-router
组件路由守卫如
beforeRouteEnter
等外部混入的钩子想在组件中class使用,需要在App.vue中注册:1
2Component.registerHooks(['beforeRouteEnter'])
//不注册也可以在Component装饰器中使用
vuex
使用TS时,
Vuex.Store
是一个泛型方法,需要传入约束state
的接口modules
选项: 从vuex
导入的Module
是泛型接口,可以用来约束对应模块对象内的state
:1
export const m1:Module<该模块state的接口, 根state的接口> = {...模块选项}
合并到组件: 可从vue-class中导入
State, Action
等装饰器,还有namespace
方法。通过:1
2
3
4
5
6
7
8
9
10const m = namespace(moduleName)
export default class 组件类 extends Vue {
//被修饰的属性会得到对应vuex模块中key对应的state值
.State(key)
属性
//若key与属性或方法名相同可以不传参
//被修饰的方法会成为对应vuex模块中key对应的action
.Action
方法
}
混入mixin
先创建一个混入的组件实例:
1 | import { Component, Vue } from 'vue-property-decorator' |
ts使用混入,要靠Mixins
方法(从vue-class-component导入)生成的类来继承:
1 | import { Mixins } from "vue-property-decorator"; |
shims-vue.d.ts
此文件在vue-cli项目中为vue添加扩展模块, 也可以自定义声明文件添加模块声明扩展:
1 | declare module '被扩展的模块' { |
扩展
vue-router
的meta
元数据类型:1
2
3
4
5
6
7
8
9//声明
declare module 'vue-router' {
interface RouteMeta extends Record<string | number | symbol, unknown> {
name: string;
title?: string;
}
}
//使用:
import type { RouteMeta } from 'vue-router';给原型挂载东西,如
$axios
:1
2
3
4
5
6import {AxiosInstance} from 'axios'
declare module 'vue/types/vue' {
interface Vue {
$axios: AxiosInstance
}
}扩展组件选项: 解决main.ts中vue选项警告(设置了
router
和store
)1
2
3
4
5
6
7
8import VueRouter from 'vue-router'
import {Store} from 'vuex'
declare module 'vue/types/optins' {
interface ComponentOptions<V extends Vue> {
router?: VueRouter
store?: Store<any>
}
}
踩坑
在vue3 + ts项目中,使用
ref
获取元素时,泛型需要这样定义:ref
挂载到原生标签,拿到的是原生dom:1
ref<HTMLElement | null>()
ref
挂载到组件,拿到的是组件实例,组件类型要使用内置接口instanceType
和类型守卫:1
ref<InstanceType<typeof 组件>>
Eslint
在ts项目中引入eslint(tslint不再更新)
安装
1
npm i -D eslint typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin
根目录创建
.eslintrc.js
配置文件1
2
3
4
5
6
7
8
9
10
11module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint',
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
],
};package.json => scripts中配置:
1
2//通过npm run lint启动
"lint": "npx eslint . --ext .js,.jsx,.ts,.tsx"
prettier
eslint可配合perttier使用,eslint负责语法监测,perttier负责代码格式的规范
安装
prettier:prettier插件的核心代码
eslint-config-prettier:解决ESLint中的样式规范和prettier中样式规范的冲突,以prettier的样式规范为准,使ESLint中的样式规范自动失效
eslint-plugin-prettier:将prettier作为ESLint规范来使用
1
npm i -D prettier eslint-config-prettier eslint-plugin-prettier
根目录创建
.prettierrc.js
文件:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16module.exports = {
//单引号
singleQuote: true,
//末尾不需要逗号 'es5'/none
trailingComma: 'es5',
//一行最大字符数
printWidth: 120,
//缩进数
tabWidth: 2,
//不用缩进用空格
useTabs: false,
//行尾分号
semi: false,
//大括号首位空格
bracketSpacing: true
};修改
.eslintrc.js
中配置prettier/@typescript-eslint
:使得@typescript-eslint中的样式规范失效,遵循prettier中的样式规范plugin:prettier/recommended
:使用prettier中的样式规范,且如果使得ESLint会检测prettier的格式问题,同样将格式问题以error的形式抛出
1
2
3
4
5
6
7
8plugins: [
'@typescript-eslint',
'prettier'
],
extends: [
'prettier',
'plugin:prettier/recommended'
]vue3 + ts项目中:
在声明文件声明ts全局类型后,其他地方使用时检测到了类型并可以顺利运行,但eslint还是当作变量报未定义波浪线。需要在根目录创建**.eslintrc.js**文件自定义配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84//vue3+ts版:
module.exports = {
root: true,
env: {
browser: true,
node: true,
es6: true,
},
parser: "vue-eslint-parser",
extends: [
"plugin:vue/recommended",
"plugin:prettier/recommended",
"prettier/@typescript-eslint",
"plugin:@typescript-eslint/recommended",
],
plugins: ["@typescript-eslint"],
parserOptions: {
parser: "@typescript-eslint/parser",
},
rules: {
"prettier/prettier": "error",
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
"array-bracket-spacing": 2,
"no-var": 2,
"no-eval": 2,
"arrow-spacing": 2,
"block-spacing": 2,
"key-spacing": 2,
"brace-style": 2,
"vue/camelcase": 2,
"vue/require-component-is": 0,
"vue/require-default-prop": 0,
"comma-dangle": [2, "always-multiline"],
"vue/eqeqeq": [2, "always", { null: "ignore" }],
"object-curly-spacing": [2, "always"],
"vue/singleline-html-element-content-newline": 0,
"vue/html-closing-bracket-newline": [
2,
{
singleline: "never",
multiline: "always",
},
],
"vue/max-attributes-per-line": 0,
"vue/html-self-closing": [
2,
{
html: {
void: "always",
normal: "never",
component: "always",
},
svg: "always",
math: "always",
},
],
// 设置 typescript-eslint 规则
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin/docs/rules
"@typescript-eslint/camelcase": 0, // 目前埋点有部分字段无法更换
"@typescript-eslint/no-non-null-assertion": 0, // 允许非空断言运算符
"@typescript-eslint/member-delimiter-style": [
2,
{
multiline: {
delimiter: "none",
requireLast: true,
},
singleline: {
delimiter: "semi",
requireLast: false,
},
},
],
"@typescript-eslint/no-unused-vars": [0, { args: "none" }], // TODO 后期逐步替换
"@typescript-eslint/interface-name-prefix": 0,
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/no-empty-function": 0,
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/no-use-before-define": 0,
"@typescript-eslint/no-explicit-any": 0, // TODO
},
};
踩坑
无法重新生命块范围变量
在tsconfig.json中,在 Commonjs 规范(
module
为commonjs
)里,没有像 ESModule 能形成闭包的「模块」概念,所有的模块在引用时都默认被抛至全局,因此当再次声明某个模块时,TypeScript 会认为重复声明了两次相同的变量进而抛错 .解决方式:
- tsconfig.json中:
esModuleInterop: true
- 使用ESModule方式导出,或在最后加
export {}
- tsconfig.json中:
新版es2015声明文件,调用promise的
resolve
必须传一个参数tsconfig.json中,
paths
只负责模块路径的检测,实际上模块路径别名还需要额外配置,比如使用module-alias:安装后在package.json中配置,以编译后的路径为准:
1
2
3
4
5
6
7
8"_moduleAliases": {
"utils": "dist/utils",
"config": "dist/config",
"/#": "dist/types"
},
"_moduleDirectories": [
"node_modules_custom"
]
- 本文作者: MR-QXJ
- 本文链接: https://mr-qxj.github.io/2021/10/01/语言/typescript/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!