Typescript 学习笔记

发表于:2022-12-02
字数统计:21.7k 字
阅读时长:54 分钟
阅读量:1022

TypeScript 概要

TypeScript 是一种由 微软 开发的自由和开源的编程语言,它是 JavaScript 的一个超集,扩展了JavaScript的语法,而且本质上向这个语言添加了可选的 静态类型 和基于类的面向对象编程。


官网:http://www.typescriptlang.org/github:https://github.com/Microsoft/TypeScript

入门教程:https://ts.xcatliu.com/


环境搭建

npm install typescript -g

以上命令会在全局环境下安装 tsc 命令,安装完成之后,我们就可以在任何地方执行 tsc 命令了。

查看版本号,输出版本号则安装成功

tsc -v

运行

先写一个简单案例 demo1.ts

let str: string = 'hello'
console.log(str)

用 tsc 命令将 ts 文件转为 js 文件,这个时候会得到一个demo1.js文件

tsc demo1.ts

然后用 node 命令运行 js 文件

node demo1.js


从上面我们可以看到运行步骤比较繁琐,下面我们安装一些工具来解决这个问题

npm install ts-node -g
npm install -D tslib @types/node

然后你就可以用 ts-node 命令来运行了

ts-node demo1.ts


静态类型

定义:一旦被定义就不可以改变其类型

静态类型分为两种,基础静态类型对象静态类型

基础静态类型:

let year: number = 2022
year = '2022'

比如现在定义了变量 year 为 number 类型,如果这个时候把它改为其它类型就会报错

对象静态类型:

// 对象类型
const user: { name: string, age: number } = {
  name: 'jack',
  age: 21
}

// 数组类型
const users: string[] = ['张三', '李四', '王五']

// 类类型
class Person { }
const jack: Person = new Person()

// 函数类型
const log: () => string = () => {
  return 'function type'
}

静态类型可以帮助我们提高代码的可靠性,使我们的代码更加健壮。


类型注解和类型推断

类型注解:

其实就是给我们的变量或者函数添加指定类型的一种约束方式

下面的代码,我们给变量指定了 number 类型,这其实就是类型注解

let year: number = 2022

类型推断:

在不定义类型的情况下,它能根据数据推断出类型这个其实就是类型推断

工作中使用问题

如果 ts 能自动分析变量类型,我们就不需要给他指定类型

如果 ts 无法分析变量类型,我们就需要给它指定类型

我们来看一个例子:

const num1 = 1
const num2 = 2
const total = num1 + num2

上面的代码 ts 可以帮我们自动推断出类型,所以不需要特意去指定类型

function getTotal(num1: number, num2: number) {
  return num1 + num2
}

上面的代码无法推断出参数的类型,所以我们需要使用类型注解来指定类型


函数参数和返回的类型注解

比如说有这样一段代码,我们的理想结果是 number 类型,由于我们在返回值加上了 '' , 这个函数最终会返回一个 string 类型的结果,这并不是我们想要的结果,而且编译器不会报错

function getTotal2(num1: number, num2: number) {
  return num1 + num2 + ''
}

我们可以给函数的返回值来定义一个类型注解,他会根据返回结果判断返回值是不是 number 类型,如果不是编译器就会报错

function getTotal2(num1: number, num2: number): number {
  return num1 + num2 + ''
}

void

表示函数无返回值

function sayHello(): void {
  console.log('hello')
}

never

never 只能在两种情况下使用

函数永远不会有返回值时

function forNever(): never {
  while (true) {}
}

函数永远会抛出一个错误时

function errFn(): never {
  throw new Error('error')
}

另一种情况:

当我们的参数是对象的时候怎么去定义类型注解呢,看下面的例子

function add({ num1, num2 }: {num1: number, num2: number}) {
  return num1 + num2
}

add({num1: 1, num2: 2})

数组类型注解

单一类型:

let numberArr: number[] = [1, 2, 3]
let stringArr: string[] = ['a', 'b', 'c']
let boolArr: boolean[] = [true, false]

多种类型:

let arr2: (number | string)[] = [1, 'a']

对象类型:

let userList: {name: string, age: number}[] = [
  { name: '张三', age: 18 },
  { name: '李四', age: 19 }
]

在工作中同一个类型有可能会在很多地方使用,这个时候我们可以使用 type alias(类型别名)来提取公共类型达到复用

type UserType = {
  name: string,
  age: number
}

let userList: UserType[] = [
  { name: '张三', age: 18 },
  { name: '李四', age: 19 }
]

元组的使用和类型约束

元组可以看作是数组的拓展,每一个数据都必须对应一个类型,它的长度是固定的

let arr: [string, number, boolean] = ["a", 2, false]


Interface(接口)

我们使用接口来定义对象的类型。除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状」进行描述


实现接口

interface User {
  name: string,
  age: number
}

let getUserInfo = (user: User) => {
  // user.age user.name// ...
}

let updateUserInfo = (user: User) => {
  // user.age user.name// ...
}

const jack = { name: 'jack', age: 18 }

getUserInfo(jack)
updateUserInfo(jack)

可选属性

只需要在属性后面加一个 ? 就可以了,表示该字段可传可不传

interface User {
  email?: string
}

只读属性

在属性前用 readonly 来指定只读属性:

interface User {
  readonly id: number
}

任意属性

任意属性的意思是我们的属性可以是任何名字,比如说叫 city、sex 都是可以的

propName 是我们自定义的,可以改成其他名称

string 表示的是属性名的类型

any 表示属性值类型

interface User {
  name: string,
  [propName: string]: any
}

const jack = { name: 'jack', city: '深圳' }

可索引属性

与使用接口描述函数类型差不多,我们也可以描述那些能够“通过索引得到”的类型

可索引类型具有一个 索引签名,它描述了对象索引的类型,还有相应的索引返回值类型

下面接口里的代码表示索引是数字, 通过索引访问对象里面的值返回数字类型

interface MyIndex {
  [index: number]: number;
}

let arr1: MyIndex = { 0: 5, 1: 8 }
let arr2: MyIndex = [3, 5]

函数接口

除了描述带有属性的普通对象外,接口也可以描述函数类型

interface User {
  name: string,
  say(): string
}

const jack = {
  name: 'jack',
  say() {
    return 'hello'
  }
}

类实现接口

我们可以通过 implements 关键字 后面跟一个接口名称让类指定接口类型

定义好后,类里面需要实现接口中有的全部功能

interface User {
  name: string,
  age: number,
  email?: string,
  say(): string,
}

class Student implements User {
  name: 'jack'
  age: 18
  say() {
    return 'hello'
  }
}

接口继承接口

接口与接口之前可以继承,继承后获得继承接口的全部功能,也可以扩展新功能

interface User {
  name: string,
  age: number,
  email?: string,
  say(): string,
}

interface SuperUser extends User {
  update(): string
}

可索引属性

与使用接口描述函数类型差不多,我们也可以描述那些能够“通过索引得到”的类型

可索引类型具有一个 索引签名,它描述了对象索引的类型,还有相应的索引返回值类型

下面接口里的代码表示索引是数字, 通过索引访问对象里面的值返回数字类型

interface MyIndex {
  [index: number]: number;
}

let arr1: MyIndex = { 0: 5, 1: 8 }
let arr2: MyIndex = [3, 5]


类的概念和使用

类继承

class User {
  content = 'Hello'
  
  say() {
    return this.content
  }
}

const user = new User()
console.log(user.say())

class Jack extends User {
  sayLove() {
    return 'I love you'
  }
}

const jack = new Jack()
console.log(jack.say())
console.log(jack.sayLove())

重写属性和方法

class Jack extends User {
  content = 'Hello jack'

  say() {
    return this.content
  }
}

调用父类中的属性和方法

使用 super 关键字可以调用父类中的属性和方法

class Jack extends User {
  say() {
    return super.say() + ' jack'
  }
}

类的访问类型

public 公开的,允许在类的内部和外部调用(默认就是public),可以不写

class Person {
  public name?: string
}

const person = new Person()
person.name = 'jack'

private 私有的,允许在类的内部使用

class Person {
  private id?: number
}

const person = new Person()
person.id = 721823123123 // 在类的外部使用,会报错

protected 在类的内部使用或者继承后的类中使用

class Person {
  protected id?: number
}

const person = new Person()
person.id = 721823123123 // 在类的外部使用,会报错

// 但是我们可以继承后的类中使用
class Teadcher extends Person {
  say() {
    this.id
  }
}

readonly 只读属性,只有在实例化的时候可以赋值,之后不可以修改

class Person {
  public readonly _name: string

  constructor(name: string) {
    this._name = name
  }
}

const person = new Person('jack')
person._name = 'ricky' // 这个时候重新赋值不被允许

类的构造函数

class Person {
  name: string;
  constructor(name: string) {
    this.name = name
  }
}

const person = new Person('jack')

如果上面的写法感觉麻烦可以这样写

class Person {
  constructor(public name: string) {
    this.name = name
  }
}

继承

子类构造函数中必须调用 super()

class Teacher extends Person {
  // 子类继承父类必须要调用 super 关键字传递属性
  constructor(public age: number) {
    super('jack')

  }
}

如果父类中没有要实现的属性和方法也需要调用 super()

class Person {
  
}

class Teacher extends Person {
  constructor() {
    super()
  }
}

类的Getter、Setter、static

getter

我们有这样一段代码,这个时候我们将属性设置为私有的,外部不能访问

class Person {
  constructor(private _age: number) {}
}

const person = new Person(18)

console.log(person._age) // 访问不到

这个时候如果想要访问可以定义一个 get 方法返回私有属性

class Person {
  constructor(private _age: number) {}

  get getAge() {
    return this._age
  }
}

const person = new Person(18)

console.log(person.getAge)

setter

如果要改变私有属性,可以在类中定义一个 set 方法来修改

class Person {
  constructor(private _age: number) {}

  get getAge() {
    return this._age
  }

  set setAge(age: number) {
    this._age = age
  }
}

const person = new Person(18)
person.setAge = 20
console.log(person.getAge)

static

上面的例子我们都是通过实例话来调用类的属性和方法,那么我们可以使用 static 关键字定义属性或者方法

定义后我们只需要使用 类名.属性名或者方法就可以调用了

class Girl {
  static age: number = 20

  static sayLove() {
    return 'I love you'
  }
}

console.log(Girl.sayLove())
console.log(Girl.age)

抽象类的只读属性使用

抽象类使用 abstract 关键字定义,抽象类中定义的方法不实现具体的逻辑,具体的逻辑交给子类去实现

abstract class Pseson {
  abstract skill()
}

class Girl extends Pseson { 
  skill() {
    // ...
  }
}

class Boy extends Pseson { 
  skill() {
    // ...
  }
}

配置文件初识 tsconfig.json

tsconfig.json文件是 TypeScript 编译器的配置文件,TypeScript 编译器可以根据它的规则来对代码进行编译。

打开终端输入 tsc -init 会生成一个 tsconfig.json 文件


配置详解

"compilerOptions": {
  "incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
  "tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
  "diagnostics": true, // 打印诊断信息 
  "target": "ES5", // 目标语言的版本
  "module": "CommonJS", // 生成代码的模板标准
  "outFile": "./app.js", // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中,即开启时应设置"module": "AMD",
  "lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
  "allowJS": true, // 允许编译器编译JS,JSX文件
  "checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
  "outDir": "./dist", // 指定输出目录
  "rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
  "declaration": true, // 生成声明文件,开启后会自动生成声明文件
  "declarationDir": "./file", // 指定生成声明文件存放目录
  "emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
  "sourceMap": true, // 生成目标文件的sourceMap文件
  "inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
  "declarationMap": true, // 为声明文件生成sourceMap
  "typeRoots": [], // 声明文件目录,默认时node_modules/@types
  "types": [], // 加载的声明文件包
  "removeComments":true, // 删除注释 
  "noEmit": true, // 不输出文件,即编译后不会生成任何js文件
  "noEmitOnError": true, // 发送错误时不输出任何文件
  "noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
  "importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
  "downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
  "strict": true, // 开启所有严格的类型检查
  "jsx": "preserve", // 指定 jsx 格式
  "alwaysStrict": true, // 在代码中注入'use strict'"noImplicitAny": true, // 不允许隐式的any类型
  "strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
  "strictFunctionTypes": true, // 不允许函数参数双向协变
  "strictPropertyInitialization": true, // 类的实例属性必须初始化
  "strictBindCallApply": true, // 严格的bind/call/apply检查
  "noImplicitThis": true, // 不允许this有隐式的any类型
  "noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
  "noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
  "noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
  "noImplicitReturns": true, //每个分支都会有返回值
  "esModuleInterop": true, // 允许export=导出,由import from 导入
  "allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
  "moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
  "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
  "paths": { // 路径映射,相对于baseUrl
    // 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
    "jquery": ["node_modules/jquery/dist/jquery.min.js"]
  },
  "rootDirs": ["src","out"], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
  "listEmittedFiles": true, // 打印输出文件
  "listFiles": true// 打印编译的文件(包括引用的声明文件)
}

有了配置文件后我们可以做很多事情

我们可以将 "removeComments": true 然后运行 tsc 命令就会编译出一个新的文件,编译后的文件中没有用注释

我们也可以将 "outDir": "./js" 然后编译后的 js 文件会被输出到新创建的 js 文件夹中


具体的配置选项放在下面

根选项

  • include:指定被编译文件所在的目录。
  • exclude:指定不需要被编译的目录。
  • extends:指定要继承的配置文件。
  • files:指定被编译的文件。
  • references:项目引用,是 TS 3.0 中的一项新功能,它允许将 TS 程序组织成更小的部分。

使用小技巧:在填写路径时 ** 表示任意目录, * 表示任意文件。


compilerOptions

定义项目的运行时期望、JavaScript 的发出方式和位置以及与现有 JavaScript 代码的集成级别。

项目选项

  • incremental:是否启用增量编译,指再次编译时只编译增加的内容,默认:false。
  • target:指定ts编译成ES的版本。
  • module:指定编译后代码使用的模块化规范。
  • lib:指定项目运行时使用的库。
  • outDir:指定编译后文件所在目录。
  • outFile:将代码编译合并成一个文件,默认将所有全局作用域中的代码合并成一个文件。
  • rootDir:指定输入文件的根目录,默认情况下当前的项目目录为根目录。
  • allowJs:是否对js文件进行编译,默认:false。
  • checkJs:是否检查js代码是否符合语法规范,当使用checkJs,必须使用allowJs,默认:false。
  • removeComments:是否移除注释,默认:false
  • noEmit:不生成编译后的文件,默认:false。
  • jsx:指定JSX代码生成用于的开发环境。
  • plugins:在编辑器中运行的语言服务插件列表。
  • declaration:是否生成相应的 .d.ts 声明文件,默认:false。
  • declarationMap:是否为每个对应的 .d.ts 文件生成一个 Map 文件,使用该功能时,需要declaration或composite配合一起使用,默认:false。
  • sourceMap:是否生成相应的Map映射的文件,默认:false。
  • composite:是否开启项目编译,开启该功能,将会生成被编译文件所在的目录,同时开启declaration、declarationMap和incremental,默认:false。
  • tsBuildInfoFile:指定增量编译信息文件的位置,使用该功能时,必须开启incremental选项。
  • importHelpers:是否将辅助函数从 tslib 模块导入,默认:false。
  • downlevelIteration:是否用于转换为旧版本的 JS 提供可迭代对象的全面支持,默认:false。
  • isolatedModules:是否将每个文件转换为单独的模块,默认:false。

严格检查

  • strict:是否启动所有严格检查的总开关,默认:false,启动后将开启所有的严格检查选项。
  • alwaysStrict:是否以严格模式解析,并为每个源文件发出"use strict",默认:false。
  • noImplicitAny:是否禁止隐式的any类型,默认:false。
  • noImplicitThis:是否禁止不明确类型的this,默认:false。
  • strictNullChecks:是否启用严格的空检查,默认:false。
  • strictBindCallApply:是否在函数上启用严格的’bind’, 'call’和’apply’方法,默认:false。
  • strictFunctionTypes:是否启用对函数类型的严格检查,默认:false。
  • strictPropertyInitialization:是否启用严格检查类的属性初始化,默认:false。

模块解析选项

  • moduleResolution:指定模块解析策略,node或classic
  • baseUrl:用于解析非绝对模块名的基本目录,相对模块不受影响。
  • paths:用于设置模块名称基于baseUrl的路径映射关系。
  • rootDirs:将多个目录放在一个虚拟目录下,运行编译后文件引入的位置发生改变,也不会报错。
  • typeRoots:指定声明文件或文件夹的路径列表
  • types:用来指定需要包含的模块,并将其包含在全局范围内。
  • allowSyntheticDefaultImports:是否允许从没有默认导出的模块中默认导入,默认:false。
  • esModuleInterop:是否通过为所有导入模块创建命名空间对象,允许CommonJS和ES模块之间的互操作性,开启改选项时,也自动开启allowSyntheticDefaultImports选项,默认:false。
  • preserveSymlinks:是否不解析符号链接的真实路径,这是为了在 Node.js 中反映相同的标志,默认:false。
  • allowUmdGlobalAccess:允许您从模块文件内部访问作为全局变量的 UMD 导出,如果不使用该选项,从 UMD 模块导出需要一个导入声明,默认:false。

Map选项

  • sourceRoot:指定调试器应定位 TypeScript 文件而不是相对源位置的位置。
  • mapRoot:指定调试器定位Map文件的位置,而不是生成的位置。
  • inlineSourceMap:是否将Map文件内容嵌套到 JS 文件中,这会导致 JS 文件变大,但在某些情况下会很方便,默认:false。
  • inlineSources:是否将 .ts 文件的原始内容作为嵌入字符串包含在 .map 文件中,默认:false。

附加检查

  • noUnusedLocals:是否检查未使用的局部变量,默认:false。
  • noUnusedParameters:是否检查未使用的参数,默认:false。
  • noImplicitReturns:检查函数是否不含有隐式返回值,默认:false。
  • noImplicitOverride:是否检查子类继承自基类时,其重载的函数命名与基类的函数不同步问题,默认:false。
  • noFallthroughCasesInSwitch:检查switch中是否含有case没有使用break跳出,默认:false。
  • noUncheckedIndexedAccess:是否通过索引签名来描述对象上有未知键但已知值的对象,默认:false。
  • noPropertyAccessFromIndexSignature:是否通过" . “(obj.key) 语法访问字段和"索引”( obj["key"]), 以及在类型中声明属性的方式之间的一致性,默认:false。

实验选项

  • experimentalDecorators:是否启用对装饰器的实验性支持,装饰器是一种语言特性,还没有完全被 JavaScript 规范批准,默认:false。
  • emitDecoratorMetadata:为装饰器启用对发出类型元数据的实验性支持,默认:false。

高级选项

  • allowUnreachableCode:是否允许无法访问的代码(undefined / true / false),默认:undefined。
  • undefined:向编辑提供建议作为警告。
  • true:未使用的标签被忽略。
  • false:引发有关未使用标签的编译器错误。
  • allowUnusedLabels:是否允许未使用的标签(undefined / true / false),默认:undefined。
  • undefined:向编辑提供建议作为警告。
  • true:未使用的标签被忽略。
  • false:引发有关未使用标签的编译器错误。
  • assumeChangesOnlyAffectDirectDependencies是否避免重新检查/重建所有真正可能受影响的文件,而只会重新检查/重建已更改的文件以及直接导入它们的文件,默认:false。
  • charset:字符集(已弃用),默认:utf8
  • declarationDir:提供一种方法来配置发出声明文件的根目录。
  • diagnostics:用于输出用于调试的诊断信息
  • disableReferencedProjectLoad:是否禁用所有可用项目加载到内存中,默认:false。
  • disableSizeLimit:为了避免在处理非常大的 JS 项目时可能出现的内存膨胀问题,TS 将分配的内存量有一个上限,默认:false。
  • disableSolutionSearching:在编辑器中搜索查找所有引用或跳转到定义等功能时,禁止包含复合项目,默认:false。
  • disableSourceOfProjectReferenceRedirect:是否禁用项目引用源重定向,默认:false。
  • emitBOM:控制TypeScript在写输出文件时是否发出字节顺序标记(BOM),默认:false。
  • emitDeclarationOnly:是否只发出.d.ts 文件,不发出.js 文件,使用该选项时,需要配合 declaration 或 composite 一起使用,默认:false。
  • explainFiles:解释文件,此选项用于调试文件如何成为编译的一部分,默认:false。
  • extendedDiagnostics:是否查看 TS 在编译时花费的时间,默认:false。
  • forceConsistentCasingInFileNames:是否区分文件系统大小写规则,默认:false。
  • generateCpuProfile:在编译阶段让 TS 发出 CPU 配置文件,只能通过终端或 CLI 调用 --generateCpuProfile tsc-output.cpuprofile 。
  • importsNotUsedAsValues:此标志控制如何 import 工作方式,有 3 个不同的选项:remove、preserve 和 error 。
  • jsxFactory:当使用经典的JSX运行时编译JSX元素时,更改.js文件中调用的函数,默认:React.createElement 。
  • jsxFragmentFactory:指定 JSX 片段工厂函数在指定了 jsxFactory 编译器选项的情况下针对 react JSX 发出时使用。
  • jsxImportSource:当在TS 4.1中使用 jsx 作为 react-jsx 或 react-jsxdev 时,声明用于导入jsx和jsxs工厂函数的模块说明符。
  • keyofStringsOnly:当应用具有字符串索引签名的类型时,此标志将类型操作符的键值更改为返回 string 而不是string | number,已弃用,默认:false。
  • listEmittedFiles:是否将编译部分生成的文件的名称打印到终端,默认:false。
  • listFiles:是否打印编译文件部分的名称,默认:false。
  • maxNodeModuleJsDepth:在node_modules下搜索并加载JavaScript文件的最大依赖深度,默认:0 。
  • newLine:指定发出文件时要使用的换行规则,CRLF (dos) 或 LF (unix)。
  • noEmitHelpers:是否使用全局作用域助手函数提供实现,并完全关闭助手函数的发出,而不是使用 importhelper 来导入助手函数,默认:false。
  • noEmitOnError:有错误时不进行编译,默认:false。
  • noErrorTruncation:是否禁止截断错误消息,已弃用,默认:false。
  • noImplicitUseStrict:是否禁止无隐式严格模式,默认:false。
  • noLib:是否禁止自动包含任何库文件,默认:false。
  • noResolve:是否禁用析后的文件添加到程序中;默认情况下,TS 会检查 import 和 reference 指令的初始文件集,并将这些解析后的文件添加到你的程序中,默认:false。
  • noStrictGenericChecks:是否禁用严格的泛型检查,默认:false。
  • out:该选项以不可预测或一致的方式计算最终文件位置,已弃用,
  • preserveConstEnums:是否禁止删除枚举常量生成代码中的声明,默认:false。
  • reactNamespace:React命名空间,使用 jsxFactory 来代替。
  • resolveJsonModule:是否解析 JSON 模块,默认:false。
  • skipDefaultLibCheck:是否跳过默认库声明文件的类型检查,默认:false。
  • skipLibCheck:是否跳过声明文件的类型检查,这可以在编译期间以牺牲类型系统准确性为代价来节省时间,默认:false。
  • stripInternal:是否禁止 JSDoc 注释中带有@internal注释的代码发出声明,默认:false。
  • suppressExcessPropertyErrors:是否禁用报告过多的属性错误,默认:false。
  • suppressImplicitAnyIndexErrors:是否抑制隐式any索引的错误,默认:false。
  • traceResolution:当尝试调试未包含模块的原因时。启用该选项让 TypeScript 打印有关每个处理文件的解析过程的信息,默认:false。
  • useDefineForClassFields:此标志用作迁移到即将推出的类字段标准版本的一部分,默认:false。

命令行

  • preserveWatchOutput:是否在监视模式下保留过时的控制台输出,而不是每次发生更改时都清除屏幕,默认:false。
  • pretty:是否使用颜色对上下文错误和消息进行样式化,默认:true。

watchOptions

配置 TypeScript 的 --watch工作方式。

监视选项

  • watchFile:监视单个文件的策略,默认:useFsEvents
  • fixedPollingInterval:以固定时间间隔每秒多次检查每个文件的更改。
  • priorityPollingInterval:每秒多次检查每个文件的更改,但使用启发式方法检查某些类型的文件的频率低于其他文件。
  • dynamicPriorityPolling:使用动态队列,其中不经常修改的文件将不那么频繁地检查。
  • useFsEvents:尝试使用操作系统/文件系统的本机事件进行文件更改。
  • useFsEventsOnParentDirectory:尝试使用操作系统/文件系统的本机事件来监听文件父目录的变化。
  • watchDirectory:在缺乏递归文件监视功能的系统下如何监视整个目录树的策略,默认:useFsEvents
  • fixedPollingInterval:以固定时间间隔每秒多次检查每个目录的变化。
  • dynamicPriorityPolling:使用动态队列,其中不经常修改的目录将不那么频繁地检查。
  • useFsEvents:尝试使用操作系统/文件系统的本机事件进行目录更改。
  • fallbackPolling:使用文件系统事件时,此选项指定当系统用完本机文件观察器和/或不支持本机文件观察器时使用的轮询策略,默认:dynamicPriorityPolling
  • fixedPollingInterval:以固定时间间隔每秒多次检查每个文件的更改。
  • priorityPollingInterval:每秒多次检查每个文件的更改,但使用启发式方法检查某些类型的文件的频率低于其他文件。
  • dynamicPriorityPolling:使用动态队列,其中不经常修改的文件将不那么频繁地检查。
  • synchronousWatchDirectory:禁用对目录的延迟监视。
  • synchronousWatchDirectory:在本机不支持递归观看的平台上同步调用回调,并更新目录观察者的状态,默认:false。
  • excludeDirectories:使用排除目录来大幅减少 --watch 期间被监视的文件数量.
  • excludeFiles:使用excludeFiles从被监视的文件中删除一组特定的文件。

typeAcquisition

类型获取仅对 JavaScript 项目很重要。

类型获取

  • enable:提供在 JavaScript 项目中禁用类型获取的配置,默认:false。
  • include:使用 include 来指定应从绝对类型中使用哪些类型。
  • exclude:提供用于禁用 JavaScript 项目中某个模块的类型获取的配置
  • disableFilenameBasedTypeAcquisition:是否禁用基于文件名的类型获取,TypeScript 的类型获取可以根据项目中的文件名推断应该添加哪些类型,默认:false。

联合类型和类型断言

联合类型

表示属性可以设置多种类型

let str: string | number;
str = 100
str = 'hello'

function say(content: string | number) {
  
}

类型断言

类型断言可以手动指定一个值的类型

1、语法

值 as 类型 或者 <类型>值

例子:

const foo = {};
foo.bar = 123; // Error: 'bar' 属性不存在于 ‘{}’
foo.bas = 'hello'; // Error: 'bas' 属性不存在于 '{}'

由于对象foo不存在任何属性,因此给属性赋值就报错了,可以通过类型断言避免此问题。

interface Foo {
  bar: number;
  bas: string;
}

const foo = {} as Foo;
foo.bar = 123;
foo.bas = 'hello';

// 另一种形式(由于这种形式和jsx容易混淆,建议使用as关键字)
const bar = <Foo>{};

使用类型断言,如果我们没有按接口约定添加属性,ts不会发出错误警告。

interface Foo {
  bar: number;
  bas: string;
}
// 没有为foo添加bar和bas属性,也没有错误警告
const foo = {} as Foo


// 没有为bar添加bar和bas属性,也没有错误警告
const bar = <Foo>{}

为了避免出现上面的问题,建议使用ts自身提供的类型推断,减少使用类型断言。

const foo: Foo = {}

使用示例

function handler(event: Event) {
  // 可以使用MouseEvent的属性和方法const mouseEvent = event as MouseEvent;
}

枚举类型

看一个例子:

enum Gender {
  Male = "男",
  Female = "女",
  Unknown = "未知",
}

console.log(Gender.Male)   // 男
console.log(Gender.Female) // 女
console.log(Gender.Unknown)// 未知

枚举类型如果没有赋值,指向默认索引,从 0 开始

enum Gender {
  Female,
  Male
}
console.log(Gender.Female) // 0
console.log(Gender.Male)  // 1

如何设置从 1 开始

enum Gender {
  Female = 1,
  Male
}
console.log(Gender.GIRL) // 1
console.log(Gender.Male)  // 2

通过索引反向找到枚举类型

enum Gender {
  Female = 1,
  Male
}
console.log(Gender[1])  // Female

函数中使用

enum Gender {
  Female = '女',
  Male = '男'
}

function getUserInfo(name: string, sex: Gender, age: number) {
  console.log(name, sex, age)
}

getUserInfo('jack', Gender.Male, 22)

泛型

函数中使用泛型

泛型指的是在定义函数/接口/类型时,不预先指定具体的类型,而是在使用的时候在指定类型限制的一种特性。

function join<T>(first: T, second: T) {
  return `${first} ${second}`
}

join<string | number>('jack', 1)
join<string>('jack', 1) // 报错,因为指定了泛型为 string

数组泛型

就是说传递进来的参数必须是数组,数组中的类型可以是 string 或者 number

function join<T>(arr: T[]) {
  return arr
}

join<string | number>(['hello', 1])


// 或者这样写也是可以的 Array<T>
function join<T>(params: Array<T>) {
  return params
}

多个泛型定义

function join<T, P>(first: T, last: P) {
  return `${first} ${last}`
}

join<string, number>('a', 1)

泛型推断

在调用的时候不需要做类型注解,自动推断出类型,不建议使用

function join<T, P>(first: T, last: P) {
  return `${first} ${last}`
}

join('a', 1)

类中使用泛型

class Animal<T> {
  name: T;
  constructor(name: T) {
    this.name = name;
  }
  action<T>(say: T) {
    console.log(say)
  }
}
let cat = new Animal('cat');
cat.action('mimi')

接口中使用泛型

// 注意,这里写法是定义的方法哦
interface Search {
  <T,Y>(name:T,age:Y):T
}

let fn:Search = function <T, Y>(name: T, id:Y):T {
  console.log(name, id)
  return name;
}
fn('li',11);//编译器会自动识别传入的参数,将传入的参数的类型认为是泛型指定的类型

泛型约束

使用接口约束泛型

interface Person {name: string
  age: number
}
function student<T extends Person>(arg:T):T {
  return arg
}

student({ name: "lili", age: 11 })

//类型 "{ name: string; }" 中缺少属性 "age",但类型 "Person" 中需要该属性
student({ name: 'lili' })

//不能将类型“string”分配给类型“number”
student({ name: "lili" , age:'11'})

泛型工具类型

Partial

partial<T> 的作用就是将某个类型中的属性全部变为可选项?

示例:

interface Person {
  name: string
  age: number
}

function student<T extends Person>(arg: Partial<T>): Partial<T> {
  return arg
}

Record

Record<K extends keyof any, T> 的作用是将K中所有的属性转换为T类型;

示例:

interface PageInfo {
  title: string
}

type Page = 'home' | 'about' | 'other'

const x: Record<Page, PageInfo> = {
  home: { title: "xxx" },
  about: { title: "aaa" },
  other: { title: "ccc" },
}

Pick

Pick<T, K extends keyof T> 的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型,

interface Todo {
  title: string,
  desc: string,
  time: string
}

type TodoPreview = Pick<Todo, 'title' | 'time'>

const todo: TodoPreview = {
  title: '吃饭',
  time: '明天'
}

Exclude

Exclude<T,U> 的作用是将某个类型中属于另一个类型的属性移除掉

示例:

type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
const t: T0 = 'b'

ReturnType

returnType<T> 的作用是用于获取函数T的返回类型

示例:

type T0 = ReturnType<() => string> // string
type T1 = ReturnType<(s: string) => void> // void
type T2 = ReturnType<<T>() => T> // {}
type T3 = ReturnType<<T extends U, U extends number[]>() => T> // number[]
type T4 = ReturnType<any> // any
type T5 = ReturnType<never> // any
type T6 = ReturnType<string> // Error
type T7 = ReturnType<Function> // Error

NameSpace 命名空间

namespace 主要用于解决命名冲突,他会在全局生成一个对象,在 namepace 内部的类都要通过这个对象访问

namespance 是跨文件的,在任何文件中都可以使用

看例子:

namespace User {
  function test(str: string) {
    console.log("test", str);
  }
}

User.test('hello') // Error 这里会报错,没有 test 这个方法

// 如果想要访问需要在方法前面加上 export 
namespace User {
  export function test(str: string) {
    console.log("test", str);
  }
}

User.test('hello') // test hello

外部文件使用命名空间

demo1.ts

如果要使用 User 这个命名空间需要在前面加上 export,如果想要访问里面的其它东西也需要在前面加上 export

export namespace User {
  export function test(str: string) {
    console.log("test", str);
  }

  export const name: string = 'jack'

  const address: string = '深圳'
}

demo2.ts

import { User } from "./demo1"

User.test('hello') 				// test hello
console.log(User.name) 		// jack
console.log(User.address) // Error 访问不到,因为没有使用 export 暴露出去


1/0