typeScript学习笔记

本文详细介绍TypeScript的安装、配置及核心功能,包括类型约束、接口、泛型等高级特性,并介绍如何利用VSCode实现自动编译,提升开发效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

安装与编译

安装

npm install -g typescript

查看版本

tsc -v

编译

tsc yourCode.ts

配置

配置文件为 tsconfig.json

  • 文件选项配置
  • files: 表示编译需要编译的单个文件列表
"files": [
  // 指定编译文件是src目录下的a.ts文件
  "scr/a.ts"
]
  • include: 表示编译需要编译的文件或目录
"include": [
  // "scr" // 会编译src目录下的所有文件,包括子目录
  // "scr/*" // 只会编译scr一级目录下的文件
  "scr/*/*" // 只会编译scr二级目录下的文件
]
  • exclude: 表示编译器需要排除的文件或文件夹
    默认排除node_modules下的文件
"exclude": [
  // 排除src目录下的lib文件夹下的文件不会编译
  "src/lib"
]
  • extends: 引入其他配置文件,继承配置
// 把基础配置抽离成tsconfig.base.json文件,然后引入
"extends": "./tsconfig.base.json"
  • compileOnSave:设置保存文件的时候自动编译
"compileOnSave": true

************* vscode中上述功能的实现需要运行一个任务 *************

↓↓↓↓↓↓ vscode下实现保存文件自动编译 ↓↓↓↓↓↓

在这里插入图片描述

  • 编译选项配置
  • compilerOptions:配置编译选项
    编译选项配置非常繁杂,有很多配置,这里只列出常用的配置。
"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, // 开启所有严格的类型检查
  "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// 打印编译的文件(包括引用的声明文件)
}

  • references 指定工程引用依赖
    如果你后端是用node写的可能用得着这个 ^ _ ^ :
    在项目开发中,有时候我们为了方便将前端项目和后端node项目放在同一个目录下开发,两个项目依赖同一个配置文件和通用文件,但我们希望前后端项目进行灵活的分别打包,那么我们可以进行如下配置:
Project
  - src
    - client //客户端项目
      - index.ts // 客户端项目文件
      - tsconfig.json // 客户端配置文件
        {
          "extends": "../../tsconfig.json", // 继承基础配置
          "compilerOptions": {
            "outDir": "../../dist/client", // 指定输出目录
          },
          "references": [ // 指定依赖的工程
            {"path": "./common"}
          ]
        }
    - common // 前后端通用依赖工程
      - index.ts  // 前后端通用文件
      - tsconfig.json // 前后端通用代码配置文件
        {
          "extends": "../../tsconfig.json", // 继承基础配置
          "compilerOptions": {
            "outDir": "../../dist/client", // 指定输出目录
          }
        }
    - server // 服务端项目
      - index.ts // 服务端项目文件
      - tsconfig.json // 服务端项目配置文件
        {
          "extends": "../../tsconfig.json", // 继承基础配置
          "compilerOptions": {
            "outDir": "../../dist/server", // 指定输出目录
          },
          "references": [ // 指定依赖的工程
            {"path": "./common"}
          ]
        }
  - tsconfig.json // 前后端项目通用基础配置
    {
      "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "strict": true,
        "composite": true, // 增量编译
        "declaration": true
      }
    }

这样配置以后,就可以单独的构建前后端项目:

  • 前端项目构建
tsc -b src/client
  • 后端项目构建
tsc -b src/server
  • 输出目录
Project
 - dist 
  - client
    - index.js
    - index.d.ts
  - common
    - index.js
    - index.d.ts
  - server
    - index.js
    - index.d.ts

类型约束

对于基本类型只需通过冒号+类型即可实现类型约束
默认情况下nullundefined当做属于任何类型,即即使声明了number类型,该变量也可以是null或者undefined
除非设置了:"strictNullChecks": true,// 不允许把null、undefined赋值给其他类型的变量,null 和 undefined 获得了它们自己各自的类型nullundefined类型

let str:string = '哈哈哈'
str = 1 //会有错误提示,因为str不是number类型

数组、元组、函数类型约束

let arrOfNums:number[] = [1,2,3]
arrOfNums.push('1')//会有错误提示,因为规定了该数组的成员只能为number类型
let user:[string,number]
user=['李雷',16]//正确
user=['李雷',16,18]//会有错误提示,因为user的成员类型、数量、位置已经被限定了

//可以这样突破限制,但是push的值也只能是元组限定的类型,即 string|number
user=['李雷',16]
user.push(1)//正确
user.push(true)//会有错误提示,因为[string,number]类型中没有boolean类型的成员

表示可选参数,可选参数后不能有确定参数,不然会出错

function add(x:number,y:number,z?:number):number{
	if(z){
		return x+y+z
	}else{
		return x+y
	}
}
//或者
const add = (x:number,y:number,z?:number):number => {
	//...	
}
//此时add有了一个类型 (x:number,y:number,z?:number | undefined):number

Interface -接口

作用:用于描述和限定对象应该由什么属性、方法构成。
readonly修饰符表示该属性和方法在定义后就不可以修改了。

interface Human{
	name:string;
	readonly age:number;
	six:boolean;
	study?:(content:string)=>void;
}

//study可有可无,因为它是可选的,但是如果你一旦有这个了,就得和interface里规定的类型一致。
//对象中多出来interface里没有的属性和方法是不可以的。
//对象中缺失interface里规定的必须有的项是不可以的。
let lilei:Human={
  name:'lilei',
  age:18,
  six:true,
  study:(content:string)=>{
    console.log('我学习了' + content)
  }
}

//不可以,因为age为readonly
lilei.age=17

一般情况下 interface 的命名用大写的 I 开头比较规范。例如:interface IHuman{…}
不过eslint有的限制会禁止你使用 I 开头来命名interface

类型推论 联合类型 类型断言

类型推论:ts的机制,在没有明确指定类型的时候,对类型做出推论

let str="哈哈哈"
str=1 //不可以,因为str在赋值时被限定为string类型

联合类型:表示变量或属性可以是多种类型中的一个 例如:string | number
注意:联合类型只能访问这些类型共有的属性和方法。

function fn(numOrStr:number | string):void{
  //联合类型只能访问这些类型共有的属性和方法。
  console.log(numOrStr.length) //不可以哦,因为number类型上没有length属性。
}

类型断言:主动断言变量可以有的一个确定类型,可解决上述问题。
as 关键字断言。

function fn(numOrStr:number | string):void{
  //类型断言
  console.log((numOrStr as string).length) //可以哦,因为断言了这个变量的类型,所以可以访问了。
}

在判断类型的条件语句中,同样可以起到上述效果
支持的关键字为typeofinstanceof

function fn(numOrStr:number | string):void{
	 if(typeof numOrStr === 'string'){
	 	console.log(numOrStr.length)//可以的哦,该条件分支自动帮我们缩小了范围,不会有错误提示
	}
}

class -类

class在ES6中已有使用,ts对它有更多的支持

访问修饰符
public:在该类、子类、实例对象都可以访问。
protected:能在该类、子类中访问,实例对象不可以访问。
private:仅能在该类中访问。

静态修饰符
static:表示该属性或方法为类的静态属性或方法,实例上就没有这个属性或方法,所以只能用类名调用,不能用this哦~

通过以下代码理解他们:

class Human {
  public name: string;
  protected age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

class Man extends Human {
  static gene:string="(*&&(&*&(*&^&*&^&"
  private wife:string | null;
  constructor(name: string, age: number, wife: string | null) {
    super(name,age)
    this.wife=wife
    // this.gene="*&&^&*^*&^%$%$$"   //this是调用不了类的静态成员的
  }

  public writeGene=(newGene:string):void=>{
    Man.gene=newGene   //想要调用gene,只能用类名来调用,因为gene是Man类型的静态属性
  } 
}

let lilei=new Man("lilei",18,null)

//console.log(lilei.wife) //不能访问,属性"wife"为私有属性,只能在类"Man"中访问。
//console.log(lilei.age) //不能访问,属性"age"受保护,只能在类"Human"及其子类中访问。
console.log(lilei.name)

接口和类的搭配使用

关键字:implements:用于类实现接口

interface Radio{
	switchRadio(trigger:boolean):void;
}
interface Battery{
	checkBatteryStatus():void;
}

class Cellphone implements Radio,Battery{
	//必须实现 Radio 和 Battery 接口
	switchRadio(trigger:boolean){
		//some code
	}
	checkBatteryStatus(){
		//some code
	}
}

接口可以继承,也是用extends关键字

interface Radio{
	switchRadio(trigger:boolean):void;
}

//此时若要实现 RadioWithBattery 接口,就要实现 switchRadio 和 checkBatteryStatus 两个方法
interface RadioWithBattery extends Radio{
	checkBatteryStatus():void;
}

枚举

对一组常量的统一管理,关键词enum

  • 基本使用
enum season{
  spring,
  summer,
  autumn,
  winter
}
console.log(season.spring) //0
console.log(season.summer) //1
console.log(season.autumn) //2
console.log(season.winter) //3
console.log(season[0]) //spring
console.log(season[1]) //summer
console.log(season[2]) //autumn
console.log(season[3]) //winter
  • 如果给其中的某些成员赋值数字

一个原则:后面成员的值为前一个成员的值+1
赋值操作会覆盖默认值,但后面的值依然遵从这个原则
请看代码

enum season{
  spring,
  summer,
  autumn = 0.5,
  winter
}
console.log(season.spring) //0
console.log(season.summer) //1
console.log(season.autumn) //0.5
console.log(season.winter) //1.5
console.log(season[0]) //spring
console.log(season[1]) //summer
console.log(season[0.5]) //autumn
console.log(season[1.5]) //winter
  • 如果值重复了会怎么样?(都是数字的情况下)

以下代码解决你的困惑

enum season{
  spring,
  summer,
  autumn = 1,
  winter
}
console.log(season.spring) //0
console.log(season.summer) //1
console.log(season.autumn) //1
console.log(season.winter) //2
console.log(season[0]) //spring
console.log(season[1]) //autumn
console.log(season[2]) //winter

//如果数字有重复,那通过数字索引来找成员名,会找到最后赋值这个数字的成员
  • 赋值其他类型会怎样?

四个原则:

1.当赋的值为boolean类型或者引用类型的时候会出错。错误为:只有数字枚举可具有 计算成员,但此表达式的类型为 XXX。如果不需要全面性检查,请考虑改用对象文本。


2.当赋值不为数字时(合法的类型还有:stringnullundefined),下一个成员必须得赋值。(即会有错误提示:枚举成员必须具有初始化表达式。)

3.如果有成员赋值了string类型,那么就不能有成员被赋值为null或者undefined,否则会有错误提示:含字符串值成员的枚举中不允许使用计算值。

4.只有成员的值为数字时可以双向映射,即:
console.log(season.spring) //0
console.log(season[0]) //spring
其余成员只能通过成员名找到相应的值。

enum menbers{
  a,
  b,
  c="哈哈哈",
  d=0.5,
  e,
  f
}
console.log(menbers.a) //0
console.log(menbers.b) //1
console.log(menbers.c) //哈哈哈
console.log(menbers.d) //0.5
console.log(menbers.e) //1.5
console.log(menbers.f) //2.5
console.log(menbers[0]) //a
console.log(menbers[1]) //b
console.log(menbers[0.5]) //d
console.log(menbers[1.5]) //e
console.log(menbers[2.5]) //f


enum menbers1{
  a,
  b,
  c=null,
  d=undefined,
  e=0.5,
  f
}
console.log(menbers1.a) //0
console.log(menbers1.b) //1
console.log(menbers1.c) //null
console.log(menbers1.d) //undefined
console.log(menbers1.e) //0.5
console.log(menbers1.f) //1.5
console.log(menbers1[0]) //a
console.log(menbers1[1]) //b
console.log(menbers1[0.5]) //e
console.log(menbers1[1.5]) //f

泛型

你可以理解成让类型参数化,以便更灵活地进行类型约束的一种方式。它的特征是一对尖括号<>。
<>中的参数表示类型

  • 泛型在函数中的使用——泛型函数
interface IT1 {
  length: number;
}
interface IT2 {
  push: (...items: any[]) => number;
}

//约束了T1,T2应该有哪些属性或方法
function fn<T1 extends IT1, T2 extends IT2>(p1: T1, p2: T2): T2 {
  p2.push(p1.length)
  return p2
}

//假如第一个参数不是个数组而是个数字就会报错,因为第一个参数类型规定为T1类型,而T1类型被限定必须有length属性
let arr = fn([1, 2, 3, 4, 5], ["a", null, 2])
console.log(arr)

//可以进一步进行约束,如下
//<>里写明的类型必须满足T1、T2的约束,而函数的2个实参必须是<>里对应的类型
let arr1 = fn<string, Array<number>>("adffewww", [1, 2, 3])
console.log(arr1)

  • 泛型在类中的使用——泛型类
class Queue<T>{
  private data = [];
  push(item:T){
    return this.data.push(item)
  }
  pop():T{
    return this.data.shift()
  }
}

//这个<>里的number就限定了queue.push的参数类型和queue.pop的返回值类型都得为number,因为在Queue中做了泛型约束
const queue =new Queue<number>()
queue.push(1)
console.log(queue.pop().toFixed())

  • 泛型在接口中的使用——泛型接口
interface KeyPair<T, U> {
  key: T
  value: U
}
//接口中各成员的类型可以灵活约束
let kp1: KeyPair<number, string> = { key: 1, value: "string" }
let kp2: KeyPair<string, number> = { key: "str", value: 2 }
  • 小知识
//这两句代码是等效的。Array是ts内置的一个泛型类
var arrTwo: number[]
var arrTwo: Array<number>

类型别名,字面量,交叉类型

  • 类型别名:给类型起给别名,关键词type
type T1 = (param1: number[]) => number;
type T2 = string | number;

let fn: T1
fn = (arr: number[]) => {
  return arr.sort((a, b) => { return a - b })[0]
}

let strOrNum: T2
strOrNum = "哈哈哈"
strOrNum = 123
  • 字面量:一种特殊的类型,是一个原始数据类型的数据。该类型的变量或属性只能是该原始数据的值。
//一个简单的字面量
let a:"哈哈哈"
a="哈哈哈" //没错它只能赋值"哈哈哈",赋值其他任何的值都是会提示错误的

//字面量和联合类型的配合使用
type T1 = "哈哈哈" | 1 | true | null | undefined
let b:T1
b="哈哈哈" //它只能赋值T1里那些字面量的值

  • 交叉类型:和联合类型对应,表示该类型为两种或两种以上类型的全部属性和方法的总和。关键字 &
interface IName {
  name: string
}
type IPerson = IName & { age: number }
let person: IPerson = { name: "李雷", age: 17 } //这两个属性,不能少一个,也不能多出来其他属性

声明文件

因为有时我们使用的有些库不是ts编写的,所以我们在使用库时需要一份声明文件XXX.d.ts
这个文件是用来声明库里的变量、方法、属性等(没有的话,当我们使用库里提供的变量、方法、属性时会出现:找不到名称XXX、XXX不存在属性XX等错误)
关键词declare

  • 全局库

声明:在全局库中,可以在全局变量中声明普通变量,函数,对象等。

interface UserInfo { 
  id: string,
  name: string,
  age: number
}
// 变量
declare var userInfo: UserInfo;
// 函数
declare function getUserName (id: string): string;
// 对象
declare namespace MyLib { 
  const $userInfo: UserInfo
}
// 类
declare class User { 
  constructor(args: UserInfo);
}
  • 全局插件

声明:一个全局插件是全局代码,它们会改变全局对象的结构。 比如,一些库往Array.prototype或String.prototype里添加新的方法。

interface String { 
  startWithSomeThing(some?: string): string
}
  • 模块化的库

模块导出为对象

声明:

// my-module/index.d.ts
// 导出类型
export interface UserType { 
  name: string,
  age: number,
}
// 常量
export const version: number;
// 方法
export function getUserName(id: string): string;
// 对象
export namespace $appState {
  function getName(): void;
}
// 如果要在全局导出一个命名空间来代表这个声明文件,则可以:
export as namespace MyModule;

导入:

// ts模块规范 
import * as MyModule from 'my-module';
// commonjs规范
const MyModule = require('my-module');
// TypeScript
import MyModule = require('my-module');
// 引入部分导出
import { version, $appState } from 'my-module';

模块导出为函数:

声明:我们可以声明多个函数实现函数的重载。

// my/index.d.ts
declare function MyFunction(name: string): MyOverloaded.NamedReturnType
declare function MyFunction(length: number): MyOverloaded.LengthReturnType

declare namespace MyOverloaded { 
  export interface LengthReturnType { 
    width: number,
    height: number,
  }
  export interface NamedReturnType { 
    firstName: string,
    lastName: string,
  }
}
export = MyFunction;

ts文件里

import MyFunction from "my";

MyFunction("哈哈哈").firstName
MyFunction(123).height

模块导出为一个类

声明

declare namespace Person {
  export interface Param {
    id: string;
    name: string;
    age: 20;
  }
}
declare class Person {
  constructor(someParam?: Person);
  getName(id: string): string;
}
export = Person;

工具类型

这些ts内置类型js中没有,是ts为了开发者能够更灵活的定义类型而存在的。
typeScript官网关于Utility types的文档
别人整理的Utility types用法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值