TypeScript
自学typescript 笔记,参考阮一峰老师ts
js:动态弱类型语言
ts:静态类型语言
优势:
-
有利于代码的静态分析
不必要运行代码就可以推断错误---静态代码分析
-
有利于发现错误
每个值,每个变量都有严格的类型约束,可以及时发现拼写,语法,方法调用错误
-
更好IDE 支持,可以做到自动语法补全,语法提示
-
提供代码文档
-
有助于代码重构
缺点:
- 丧失了动态类型的代码灵活性。
动态类型有非常高的灵活性,给予程序员很大的自由,静态类型将这些灵活性都剥夺了
- 增加了编程工作量。
有了类型之后,程序员不仅需要编写功能,还需要编写类型声明,确保类型正确。这增加了不少工作量,有时会显著拖长项目的开发时间。
- 更高的学习成本。
类型系统通常比较复杂,要学习的东西更多,要求开发者付出更高的学习成本。
- 引入了独立的编译步骤。
原生的 JavaScript 代码,可以直接在 JavaScript 引擎运行。添加类型系统以后,就多出了一个单独的编译步骤,检查类型是否正确,并将 TypeScript 代码转成 JavaScript 代码,这样才能运行。
- 兼容性问题。
TypeScript 依赖 JavaScript 生态,需要用到很多外部模块。但是,过去大部分 JavaScript 项目都没有做 TypeScript 适配,虽然可以自己动手做适配,不过使用时难免还是会有一些兼容性问题。
类型声明
类型推断
ts 设计思想是类型声明是可选的,可加可不加
tsc 编译器
全局安装之后可以使用tsc 进行编译
部分参数
-
--outFile 将多个Typescript 脚本编译 成一个 javascript 文件
tstsc file1.ts file2.ts --outFile app.js
-
--outDir
编译结果默认保存在当前目录,
--outDir
可以指定存放目录tstsc app.ts --outDir dist
-
--target
tsc 默认将ts 编译为es3 版本,
--target
指定编译版本tstsc --target es2015 app.ts
-
--noEmitNoError 当代码有错误时 ts 会进行提示,但是还有产生编译产物,如果希望一旦报错就停止编译,不生产编译产物,可以使用
--noEmitOnError
参数tstsc --noEmitOnError app.ts
-
--noEmit
只检查类型是否有编译错误,不会生成app.js
tstsc --noEmit app.ts
tsconfig.json
ts 允许将tsc 的编译参数,写在配置文件 tsconfig.json 中,tsc 会自动读取该文件
ts 特殊的类型:any、unknown、never 类型
any
any 类型表示没有任何限制,该类型的变量可以赋予任意类型的值
ts
let x:any
x = 1;
x= 'hello'
x = false
变量类型一旦设置了any,ts 实际上就会关闭类型检查,即使有明显的类型错误,只要句法正确,都不会报错。
由于这个原因,应该尽量避免使用any
类型,否则就失去了使用 TypeScript 的意义。
实际开发中,any
类型主要适用以下两个场合。
(1)出于特殊原因,需要关闭某些变量的类型检查,就可以把该变量的类型设为any
。
(2)为了适配以前老的 JavaScript 项目,让代码快速迁移到 TypeScript,可以把变量类型设为any
。有些年代很久的大型 JavaScript 项目,尤其是别人的代码,很难为每一行适配正确的类型,这时你为那些类型复杂的变量加上any
,TypeScript 编译时就不会报错。
总之:any 类型可以看成是所有其他类型的全集,包含了一切可能的类型,typescript 将这种类型称为 顶层类型,意为涵盖了所有下层
TypeScript 提供了一个编译选项noImplicitAny
,打开该选项,只要推断出any
类型就会报错。
弊端:
- 关闭了类型检查
- 污染问题 因为他可以赋值给任意类型的变量
unknown 类型
为了解决 any 类型污染其他类型变量的问题,ts 在3.0版本之后推出了unknown 类型。他与any 含义,表示类型不确定,可能是任意类型,但是他有一些限制,可以视为严格版本的any
与 any 相同之处。在于所有类型都可以分配给unknown 类型
ini
let x:unknown;
x = true; // 正确
x = 42; // 正确
x = 'Hello World'; // 正确
不同之处:
他不能直接使用,有一下几个限制:
-
unknown 类型变量不能赋值给其他类型的变量
tslet v:unknown = 123 let v1:boolean = v // 报错 let v2:number = v // 报错
-
不能直接使用unknown 类型变量的方法 和属性
typescript
let v1:unknown = { foo: 123 };
v1.foo // 报错
let v2:unknown = 'hello';
v2.trim() // 报错
let v3:unknown = (n = 0) => n + 1;
v3() // 报错
- 运算是有限制的,只能进行比较运算,取反运算,
typeof
运算,instanceof
运算
ini
let a:unknown = 1;
a + 1 // 报错
a === 1 // 正确
怎么才可以使用unknown 类型变量呢? 就是使用"类型缩小",就是判断具体的类型
ts
let a:unknown = 1
if(typeof a === 'number'){
let r = a+10
}
let s:unknown = 'hello';
if (typeof s === 'string') {
s.length; // 正确
}
这样设计的目的是,只有明确unknown 变量的实际类型,才迅速使用,防止像any 那样可以随意使用 ,污染其他变量
总之 unknown 可以看做更安全的any,一般来说,凡事需要设为any 类型的地方,通常都应该考虑设为unknown 类型,
unknown 类型只可以赋值给any,unknown 类型
unknown 也可以视为所有的其他类型 的全集(除了any),他和any 一样,也属于Ts 顶层类型
never 类型
空值类型 即该类型为空,不包含任何值
使用场景:主要在一些类型运算之中,保证类型运算的完整性,另外,不可能返回值的函数,返回值就可以写成never 类型
typescript
function f():never {
throw new Error('Error');
}
let v1:number = f(); // 不报错
let v2:string = f(); // 不报错
let v3:boolean = f(); // 不报错
never 可以赋值给其他任意其他类型,因为never 是底层类型
基本类型
js 有8中数据类型
- string
- boolean
- number
- bigint
- object
- symbol
- undefined
- null
其中 原始数据类型为 string boolean number,bigint,引用数据类型为object
null/undefined 是两种独立类型,他们各自都有一个值
undefined 只包含 值undefined 表示未定义
null 表示空值
如果关闭了noImplicitAny 和strictNullChecks 他们类型会被推断为any
包装对象
js
'hello',chatAt(1) // e
字符串 hello 执行了chatAt() 方法时,原始数据类型本身乜有方法,这段代码可以执行,因为在调用方法时,字符串会自动转化为"包装对象",chatAt() 方法定义在包装对象上
注意:Symbol ,bigInt 无法直接获取他们的包装对象,因为他们不能作为构造函数直接使用,但是剩下三种可以
- String()
- Number()
- Boolean()
包装对象类型与字面量类型
javascript
'hello' // 字面量
new String('hello') // 包装对象
为了区分这两种情况,TypeScript 对五种原始类型分别提供了大写和小写两种类型。
- Boolean 和 boolean
- String 和 string
- Number 和 number
- BigInt 和 bigint
- Symbol 和 symbol
大写:包装对象+字面量 小写:字面量
Object 类型和object 类型
Object
大写Object 类型js 广义对象,都可以转化为Object 的值(除了undefined
和null
)
ini
let obj:Object;
obj = true;
obj = 'hi';
obj = 1;
obj = { foo: 123 };
obj = [1, 2];
obj = (a:number) => a + 1;
object
小写代表js 狭义对象只能代表 字面量的对象(数组,对象,函数)
ini
let obj:object;
obj = { foo: 123 };
obj = [1, 2];
obj = (a:number) => a + 1;
obj = true; // 报错
obj = 'hi'; // 报错
obj = 1; // 报错
undefined null
特殊的类型 可以赋值给其他的类型,并不会报错
开启tsc 配置 strictNullChecks
undefined 和null 就不能赋值给其他类型的变量
值类型
单个值也是一种类型
ts 在进行类型推断时 ,遇到const 声明的变量,如果代码中没有注明类型,就会推断改变量为值类型
js
let x:'hello'
x = 'hello'
const x = 'https' // x 的类型为'https'
const y:string = 'https' // x:string
const x= {foo:1} // x:{foo:number}
联合类型 |
type A = number|string
交叉类型 &
一般用于type (类型别名合并)
ts
type A = {foo:number} & {bar:number}
类型别名 type
用来定义类型别名
类型别名不可以重名 类型别名是块级作用域
typeof 运算符
typeof 在js 中是一个 一元运算符,返回一个字符串,代表操作数的类型
在ts 中 typeof 操作的依然是一个值 ,返回的是 该值的类型
类型兼容
ini
type T = number|string;
let a:number = 1;
let b:T = a;
a 类型可以赋值给b 类型 ,因为a 类型是b 类型的子类型,也就是说number 类型是 number|string 的子类型
class
类 (class)是面向对象的编程的基本构件,封装了属性和方法
属性类型
tsx
class Point{
x:nubmer;
y:number
}
注意点:
-
如果给出初始值,可以不写类型
-
ts 配置项:
strictProperInitialization
打开后必须设置初始值,默认是打开的 -
可以使用非空断言解决上面问题
-
属性前面增加readonly 修饰符,表示该属性为只读的,在够赞函数中可以修改只读属性值
方法类型
类的方法类型就是普通函数 与声明函数类型一致
存取器方法
getter/setter
kotlin
class C{
_name = ''
get name(){
return this._name
}
set name(value){
this._name = va;ue
}
}
注意点:
- 如何只有get 没有set ,那么该属性自动会成为只读属性
- 5.1 版本之前get、set 参数类型属性值一致
- get、set 可访问性一致
class 类型
ts 中类本身就是一种类型,代表该类的实例类型,而不是class 的自身类型
js
class Color{
name:string;
contructor(name:string){
this.name = name
}
}
const green:Color = new Color('green')
上面示例中,定义了一个类Color
。它的类名就代表一种类型,实例对象green
就属于该类型。
类的自身类型:
如果想获取类的自身类型,最简单的方法就是用 typeof
js
class PointClass{
x:number;
y:number;
contructor(x:number,y:number){
this.x = x;
this.y = y
}
}
function createPoint(
PointClass:typeof Point,
x:number,
y:number
):Point{
return new PointClass(x,y)
}
可访问性修饰符
public、private、protected 这三个修饰符写在属性和方法前面
-
public
外部实例可以调用
-
private 私有成员,只有当前类的内部可以访问,子类和类的实例都不可以进行访问
但是 使用 【】 和in 都可以访问到私有成员 ,es2022 引入了# 关键字写法,
jsclass A { #x = 1; } const a = new A(); a['x'] // 报错
构造方法可以是私有的
jsclass Singletion { private static instance?:Singleton; private constructor(){} static getInstance(){ if(!Singleton.instance){ Singletion.instance = new Singleton() } return Singleton.instance } } const s = Singleton.getInstance()
-
protected
子类可以使用
实例属性的简写形式
ts
class Point {
x:number;
y:number;
constructor(x:number, y:number) {
this.x = x;
this.y = y;
}
}
// 简写
class Point {
constructor(
public x:number,
public y:number
) {}
}
const p = new Point(10, 10);
p.x // 10
p.y // 10
静态成员
使用static
关键字
静态成员只能通过类本身使用的成员,不能通过实例对象对使用
抽象类 抽象成员
抽象类只能作为基类,不能被实例,只能当做其他类的模板
抽象属性:抽象类可以有已经实现好的属性和方法,也可以还未实现的属性和方法,后者叫做抽象成员
(1)抽象成员只能存在于抽象类,不能存在于普通类。
(2)抽象成员不能有具体实现的代码。也就是说,已经实现好的成员前面不能加abstract
关键字。
(3)抽象成员前也不能有private
修饰符,否则无法在子类中实现该成员。
(4)一个子类最多只能继承一个抽象类。
this
decalre
decalre 关键字告诉编译器 ,某个类型是存在的,可以在当前文件中使用
作用:让当前文件可以使用其他文件的声明的类型
特点:
- 只是通知编译器某个类型是存在的,不用给出具体实现
- 只能描述已经存在的变量和数据结构,不能用来声明新的变量和数据结构
decalre 可以描述以下类型:
- 变量 const let var
- type interface
- class
- enum
- function 函数
- module
- 命名空间 namespace
decalre 变量
可以给出外部变量的类型描述
例如当前脚本使用了外部脚本定义的全局变量
js
x = 123 // 报错
declare let x:number
declare let x:number = 1 // 报错 不可以进行赋值
decalre 类
js
declare class C {
// 静态成员
public static s0():string;
private static s1:string;
// 属性
public a:number;
private b:number;
// 构造函数
constructor(arg:number);
// 方法
m(x:number, y:number):number;
// 存取器
get c():number;
set c(value:number);
// 索引签名
[index:string]:any;
}
declare namespace ,module
如果想把变量,函数,类 组织在一起
tsx
declare namespace AnimaliLib{
class Animal{
construcotr(name:string);
eat():void;
sleep():void
}
type Animals = 'Fish' | "Dog"
}
declare module AnimalLib{
class Animal{
construcotr(name:string);
eat():void;
sleep():void
}
type Animals = 'Fish' | "Dog"
}
ts
import { Foo as Bar } from 'moduleA';
declare module 'moduleA' {
interface Bar extends Foo {
custom: {
prop1: string;
}
}
}
上面示例中,从模块moduleA
导入了Foo
接口,将其重命名为Bar
,并用 declare 关键字为Bar
增加一个属性custom
。
某些第三方模块,原作者没有提供接口类型,可以在自己的脚本顶部加上一行命令
js
declare mudule '模块名'
declare module 'hot-new-module'
加上上面的命令以后,外部模块即使没有类型声明,也可以通过编译。但是,从该模块输入的所有接口都将为any
类型。
decalre global
为js 引擎的原生对象添加属性和方法,可以用declare global{} 语法
注意:declare global 必须用在模块里面
tsx
export {}
declare global{
interface String{
toSmalString():string
}
}
// 实现
String.prototype.toSmalString = ():string =>{
return ''
}
// 例子2
declare global {
interface window {
myAppConfig:object;
}
}
const config = window.myAppConfig;
declare module 用于类型声明文件
我们可以为每个模块脚本定义一个d.ts 把该脚本用到的类型定义都定义在这个文件里面。但是更方面的做法是为了整个项目,定义一个大的.d.ts文件,在这个里面 使用declare module 定义每个模块脚本的类型
tsx
declare module "url" {
export interface Url {
protocol?: string;
hostname?: string;
pathname?: string;
}
export function parse(
urlStr: string,
parseQueryString?,
slashesDenoteHost?
): Url;
}
declare module "path" {
export function normalize(p: string): string;
export function join(...paths: any[]): string;
export var sep: string;
}
上面示例中,url
和path
都是单独的模块脚本,但是它们的类型都定义在node.d.ts
这个文件里面。
使用时,自己的脚本使用三斜杠命令,加载这个类型声明文件。
ini
/// <reference path="node.d.ts"/>
如果没有上面这一行命令,自己的脚本使用外部模块时,就需要在脚本里面使用 declare 命令单独给出外部模块的类型。
类型运算符
keyof
接收一个
对象类型
作为参数,返回该对象的所有键名组成的联合类型
知识点:
-
keyof any 时返回 string|number|symbol 因为对象的键名只有三种类型
-
对于没有自定义键名的类型,返回never
typescripttype keyT = keyof object // never
-
如果对象属性名为索引形式,keyof 会返回属性名的索引类型
typescriptinterface T { [prop:number]:number } type KeyT = keyof T // number // 示例2 interface T { [prop:string]:number } type KeyT = keyof T // number | string
在实例2 中javascript 属性名为字符串时,包含了属性名为数值情况,因为数组属性名会自动转化为字符串
-
keyof 数组或元组时
jstype Result = keyof ['a', 'b', 'c']; // 返回 number | "0" | "1" | "2" // | "length" | "pop" | "push" | ···
上面示例中,keyof 会返回数组的所有键名,包括数字键名和继承的键名。
用途:
- keyof 运算符往往用于精确表达对象的属性类型
tsx
function<Obj,K extends keyof Obj>(obj:Obj,key:K):Obj[K]{
return obj[key]
}
-
做属性映射
tsxtype NewProps<Obj> = { [Prop in keyof Obj]:boolean } type MyObj = {foo:number} type NewObj = newProps<MyObj>
- 去掉修饰符 + 增加修饰符
tsxtype MyProps<Obj>{ [props in keyof Obj]-?:Obj[props] } type MyObj = { foo?:number; } // {foo:number} type NewObj = MyProps<MyObj>
in
js:判断属性 是否在包含在对象中 ts : 用于遍历 联合类型每一个成员类型
[ ] 运算符
用于取出对象的键值类型
extends ...?: 条件运算符
ts 推出了 类似 js 的?:三元运算符 ,但多出了extends 关键字
条件运算符extends...?:
可以根据当前类型是否符合某种条件,返回不同的类型。
tsx
T extends U ? X : Y
上面的例子中:判断类型T 是否可以赋值 给U 类型,即 T 是U 的子类型,如果是 类型为 X ,否则为 Y
注意点:
如果 需要判断的类型是一个联合类型,那么条件运算符会展开这个联合类型
tsx
(A | B) extends U?X:Y
//等价于
(A extends U ? X : Y) |(B extends U ? X : Y )
infer
用来推断定义泛型里面推断出来的类型参数,而不是外部传入的类型参数
注意点:
-
用于在extends 条件判断中
-
用于条件判断中的为真的 值
is
函数返回值的判定 限定 返回值 和参数之间的关系
js
function isFish(
pet:Fish | Bird
):pet is Fish{
return (pet as Fish).swim !== undefined
}
类型映射
映射:指将一种类型 安装映射规则,转换成另一种类型 ,通常用于对象类型
举例来说,现有一个类型A
和另一个类型B
。
ts
type A = {
foo: number;
bar: number;
};
type B = {
foo: string;
bar: string;
};
上面示例中,这两个类型的属性结构是一样的,但是属性的类型不一样。如果属性数量多的话,逐个写起来就很麻烦。
使用类型映射,就可以从类型A
得到类型B
。
ts
type A = {
foo: number;
bar: number;
};
type B = {
[prop in keyof A]: string;
};
类型工具
-
Awaited
取出 Promise 的返回值类型
ts// string type A = Awaited<Promise<string>>
-
ContructorParameters 提前构造方法的 Type 的 参数类型,组成一个元组
ts
// [x:string,y:string]
type T1 = ConstructorParameters<
new (x:string,y:string) => object
>
// [x?:string | undefined]
type T2 = ConstructorParamters<
new (x?:string) => object
>
-
Exclude<UnionType,ExcludeMembers> 从联合类型中删除某些类型成员,组成一个新的类型 返回
jstype T1 = Exclude<'a'|'b'|'c', 'a'>; // 'b'|'c' type T2 = Exclude<'a'|'b'|'c', 'a'|'b'>; // 'c' type Exclude<T,U> = T extends U?never:T; // 首先判断 T 是否兼容 U 是:返回never 类型,(因为never 类型是其他类型的子类型,它跟其他类型组成联合类型时,可以从联合类型中 删除)
-
Extrace<UnionType,Union>
用来 从联合类型 (UnionType)中,提取指定类型 Union 中,组成一个新类型返回,功能与 Exclude<T,U> 正好相反
ts
type T1 = Extract<'a'|'b'|'c','a'> // 'a'
type T2 = Extract<'a'|'b'|'c', 'a'|'b'>; // 'a'|'b'
type T3 = Extract<'a'|'b'|'c', 'a'|'d'>; // 'a'
// 实现
type Extrace<T,U> = T extends U? T : never
-
InstanceType
提取 构造函数的返回值的类型
tstype T = InstanceType< new () => object >
上面示例中,类型参数是一个构造函数
new () => object
,返回值是该构造函数的实例类型(object
)。 -
NonNullable
从联合类型中 Type 删除 null 和 undefined 类型,组成一个新类型返回,也就是返回 Type 的非空类型版本
tstype T2 = NonNullable<string[]|null|undefined>; // string[]
-
Omit<Type,Keys>
Omit<Type,keys> 用来从对象类型Type 中 删除指定的属性 keys, 组成一个新的对象类型返回
tsinterface A { x:number y:number } type T1 = Omit<A,'x'> // {y:number} type T2 = Omit<A,'y'> // {x:number}
OmitThisParameter
从 函数类型中 移除this 参数
9. Paramters
从函数类型 中提取 参数类型
js
type T1 = Parameters<() => string>; // []
type T2 = Parameters<(s:string) =>void> // [s:string]
-
Partial 返回一个新类型,将参数类型Type 的所有属性变为可选属性
tsinterface A { x:number y:number } type T = Partial<A,> // {x?:number,y?:number} // 具体实现: type Partial<T> = { [p in keyof T]?:T[p] }
-
Pick<Type ,Keys> 返回:一个新的对象类型, 第一个参数Type 为一个对象类型,第二个参数 keys 是 Type 里面被选定的键名
jsinterface A { x:number; y:number } type T = Pick<A,x> // {x:number} type T1 = Pick<A,'x' | 'y'> // {x:number,y:number} // 实现: Type Pick<T,K extends keyof T> = { [p in k]:T[p] }
-
Readonly 返回一个新的类型,将参数类型Type 的所有属性变为 只读属性
go
```ts
typescript
interface A {
x:number;
y?:number
}
// {readonly x:number,readonly y?:number}
type T = Readonly<A>
```
- Record<keys,Type> 返回一个对象类型,参数Keys 用作键名,参数Type 用作键值类型
scala
```tsx
type T = Record<'a',number> // {a:number}
type T1 = Record<'a'|'b',number> // {a:number,b:number}
type T2 = Record<'a',number|string> // {a:number|string}
```
其中 keys 的类型必须兼容 `string|number|symbol`否则不能用键名,
实现原理:
```ts
type Record<K extends string|number|symbol,T> = {
[p in K]:T
}
```
- Required
python
返回一个新的类型:将参数类型Type 的所有属性变为 必选属性,他与Partial 作用相反
js
interface A{
x?:number;
y:number;
}
type T = Required<A> // {x:number,y:number}
具体实现:
ts
type Required<T> = {
[p in keyof T] -?:T[p];
}
-? 去除可选属性的问号,+? 增加可选属性的问号
-
ReadonlyArray
用来生成一个只读数组类型,类型参数Type表示数组成员的类型
tsconst values:ReadonlyArray<string> = ['a','b','c'] values[0] = 'x'// errOr values.push('x') //error
具体实现:
tsinteraface ReadonlyArra<T> { readonly length:number readonly [n:number]:T }
-
ReturnType 提取函数类型Type 的返回值类型
typescripttype T1 = ReturnType<() => string> //string type T2 = ReturnType<() =>{a:string;b:string}> // {a:string;b:string}
具体实现:
typescripttype ReturnType<T extends (...args:any) => any> = T exends (...args:any) => infer R?R:any
## `ThisParameterType<Type>`
ThisParameterType<Type>
提取函数类型中this
参数的类型。
typescript
function toHex(this: Number) {
return this.toString(16);
}
type T = ThisParameterType<typeof toHex>; // number
字符串类型工具
TypeScript 内置了四个字符串类型工具,专门用来操作字符串类型。这四个工具类型都定义在 TypeScript 自带的.d.ts
文件里面。
它们的实现都是在底层调用 JavaScript 引擎提供 JavaScript 字符操作方法。
-
Uppercase<StringType>
Uppercase<StringType>
将字符串类型的每个字符转为大写。initype A = 'hello'; // "HELLO" type B = Uppercase<A>;
上面示例中,
Uppercase<T>
将 hello 转为 HELLO。
Lowercase<StringType>
Lowercase<StringType>
将字符串的每个字符转为小写。
ini
type A = 'HELLO';
// "hello"
type B = Lowercase<A>;
上面示例中,Lowercase<T>
将 HELLO 转为 hello。
-
Capitalize<StringType>
Capitalize<StringType>
将字符串的第一个字符转为大写。initype A = 'hello'; // "Hello" type B = Capitalize<A>;
上面示例中,
Capitalize<T>
将 hello 转为 Hello。
Uncapitalize<StringType>
Uncapitalize<StringType>
将字符串的第一个字符转为小写。
ini
type A = 'HELLO';
// "hELLO"
type B = Uncapitalize<A>;
上面示例中,Uncapitalize<T>
将 HELLO 转为 hELLO
注释指定
typescirpt 接受一些注释指令
采用 js 双斜杠注释的形式,向编译器发出命令
1. // @ts-nocheck
// @ts-nocheck
告诉编译器不对当前脚本进行类型检查,可以用于 TypeScript 脚本,也可以用于 JavaScript 脚本。
ini
// @ts-nocheck
const element = document.getElementById(123);
上面示例中,document.getElementById(123)
存在类型错误,但是编译器不对该脚本进行类型检查,所以不会报错。
2. // @ts-check
如果一个 JavaScript 脚本顶部添加了// @ts-check
,那么编译器将对该脚本 进行类型检查,不论是否启用了checkJs
编译选项。
ini
// @ts-check
let isChecked = true;
console.log(isChceked); // 报错
上面示例是一个 JavaScript 脚本,// @ts-check
告诉 TypeScript 编译器对其进行类型检查,所以最后一行会报错,提示拼写错误。
3. // @ts-ignore
// @ts-ignore
告诉编译器不对下一行代码进行类型检查,可以用于 TypeScript 脚本,也可以用于 JavaScript 脚本。
4.// @ts-expect-error
// @ts-expect-error
主要用在测试用例,当下一行有类型错误时,它会压制 TypeScript 的报错信息(即不显示报错信息),把错误留给代码自己处理。
scss
function doStuff(abc: string, xyz: string) {
assert(typeof abc === "string");
assert(typeof xyz === "string");
// do some stuff
}
// @ts-expect-error
expect(() => {
doStuff(123, 456);
}).toThrow();
上面示例是一个测试用例,倒数第二行的doStuff(123, 456)
的参数类型与定义不一致,TypeScript 引擎会报错。但是,测试用例本身测试的就是这个错误,已经有专门的处理代码,所以这里可以使用// @ts-expect-error
,不显示引擎的报错信息。
如果下一行没有类型错误,// @ts-expect-error
则会显示一行提示。
arduino
// @ts-expect-error
console.log(1 + 1);
// 输出 Unused '@ts-expect-error' directive.
上面示例中,第二行是正确代码,这时系统会给出一个提示,表示@ts-expect-error
没有用到。
tsconfig.json
tsconfig.json 在根目录中定义ts 配置信息
执行tsc 命令 --init 参数自动生成tsconfig
csharp
tsc --init
也可以 使用别人大神 预先写好的tsconfig.json 文件,npm 中@tsconfig 名称下有很多别人写好的tsconfig.json 样本,
例如 @tsconfig/recommended
@tsconfig/node16
tsconfig.json
的一级属性并不多,只有很少几个,但是compilerOptions
属性有很多二级属性。下面先逐一介绍一级属性,然后再介绍compilerOptions
的二级属性,按照首字母排序。
include
include
属性指定所要编译的文件列表,既支持逐一列出文件,也支持通配符。文件位置相对于当前配置文件而定。
typescript
{
"include":['src/**/*',"test/**/*"]
}
如果不指定文件后缀名,默认包括.ts
、.tsx
和.d.ts
文件。如果打开了allowJs
,那么还包括.js
和.jsx
。
exclude
exclude
属性是一个数组,必须与include
属性一起使用,用来从编译列表中去除指定的文件 。它也支持使用与include
属性相同的通配符。
extends
tsconfig.json 可以继承另一个 tsconfig.json 文件的配置 extends
属性也可以继承已发布的npm 模块里面的tsconfig 文件
typescript
{
"extends":"@tsconfig/node12/tsconfig.json"
}
自己定义的会覆盖extends 继承过来的配置
files
files
属性 指定编译的文件列表,如果有一个文件不存在就会报错
数组,排在前面的会先编译
json
{
"files":[a.ts,b.ts]
}
如果文件较多,建议使用include 和exclude
referneces
references
属性可以用来定义项目之间的引用关系,并按照指定的顺序进行编译。这对于大型项目或包含多个子项目的项目结构非常有用。
下面是一个示例,说明如何在tsconfig.json
中使用references
属性:
json
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"outDir": "dist"
},
"references": [
{ "path": "./shared" },
{ "path": "./client" },
{ "path": "./server" }
]
}
在上面的示例中,有一个主项目,同时还有三个子项目:shared
、client
和server
。通过在references
数组中添加对子项目的引用,我们可以告诉TypeScript编译器项目之间的依赖关系。
编译器会按照references
数组中的顺序编译这些项目。在编译主项目之前,它会先编译依赖的子项目。这样确保了编译顺序的正确性,并且在编译过程中能够正确处理项目之间的依赖关系。
通过references
属性,我们可以在大型项目中更好地管理和组织代码,提高编译效率,并减少潜在的编译错误。
compileOptions
该属性用来指定编译行为,如果该属性省略,这时编译器会使用默认设置
1.allowjs
当你在 TypeScript 项目中启用 "allowJs" 选项后,TypeScript 编译器将会接受并编译 JavaScript 文件,而不仅仅是 TypeScript 文件(.ts 文件)。这意味着你可以在 TypeScript 项目中直接引入和使用 JavaScript 模块。
使用 "allowJs" 选项的好处包括:
-
平滑迁移:如果你正在进行从 JavaScript 到 TypeScript 的迁移,启用 "allowJs" 可以让你逐渐将 JavaScript 文件转换为 TypeScript 文件,而不需要一次性全部转换完毕。这样可以减少迁移的复杂度和风险。
-
利用现有 JavaScript 生态系统:通过允许使用 JavaScript 文件,你可以直接使用来自 JavaScript 生态系统的现有库和工具,无需额外的转换或重新实现。
-
扩展能力:TypeScript 中的类型检查和静态分析功能可以为 JavaScript 文件提供一些类型推断和错误检查的好处。这可以帮助你在使用 JavaScript 文件时捕获一些潜在的错误。
要启用 "allowJs" 选项,在 tsconfig.json 文件中的 "compilerOptions" 部分添加以下配置:
json
{
"compilerOptions": {
"allowJs": true
}
}
需要注意的是,尽管启用了 "allowJs" 选项,TypeScript 仍然会将 JavaScript 文件视为动态类型,并且不会对其进行类型检查。如果你需要享受 TypeScript 的完整类型检查功能,建议将 JavaScript 文件逐步转换为 TypeScript 文件。
baseUrl
指定ts 项目的基准目录 默认值为当前目录 ./
baseUrl
为当前目录./
。那么,当遇到下面的语句,TypeScript 将以./
为起点,寻找hello/world.ts
。
javascript
import { helloWorld } from "hello/world";
-
checkJs
设置对js 文件进行检查,打开这个属性,相当于 自动设置allowJS:true,等于在js 脚本的头部添加 //@ts-check 命令 -
jsx
设置 jsx 如何处理
.txs
文件preserve
:保持 jsx 语法不变,输出的文件名为.jsx
。react
:将<div />
编译成React.createElement("div")
,输出的文件名为.js
。react-native
:保持 jsx 语法不变,输出的文件后缀名为.js
。react-jsx
:将<div />
编译成_jsx("div")
,输出的文件名为.js
。react-jsxdev
:跟react-jsx
类似,但是为_jsx()
加上更多的开发调试项,输出的文件名为.js
。
json{ "compilerOptions": { "jsx": "preserve" } }
-
lib
编译时可访问的 JavaScript 标准库。
lib
值是一个数组,描述项目需要加载的 TypeScript 内置类型描述文件,跟三斜线指令/// <reference lib="" />
作用相同。 -
SourceMap
编译时是否生成 sourcemap 文件 -
module
module
指定编译产物的模块格式。它的默认值与target
属性有关,如果target
是ES3
或ES5
,它的默认值是commonjs
,否则就是ES6/ES2015
。 -
target
target
指定编译出来的 JavaScript 代码的 ECMAScript 版本,比如es2021
,默认是es3
。
该选项指定了编译后的 JavaScript 代码的目标版本。它决定了 TypeScript 编译器将生成哪个 ECMAScript(JavaScript)版本的代码。常用的选项包括:
- "es5":将 TypeScript 编译为 ECMAScript 5 兼容的 JavaScript 代码。
- "es6":将 TypeScript 编译为 ECMAScript 2015 兼容的 JavaScript 代码。
- "es2017":将 TypeScript 编译为 ECMAScript 2017 兼容的 JavaScript 代码。
- "esnext":将 TypeScript 编译为支持最新 ECMAScript 标准的 JavaScript 代码。