TypeScript学习笔记

TypeScript概述和环境

TypeScript(简称:TS)是JavaScript的超集(JS有的TS都有)。

TypeScript = Type + JavaScript(为JS添加了类型系统



安装解析TS的工具包typescript

image-20210703202831765

安装步骤:

  1. 打开VSCode终端。

  2. 输入安装命令:npm i -g typescript 敲回车,来安装(注意:需要联网)。

typescript:就是用来解析TS的工具包。提供了tsc命令,实现了TS —> JS的转化。
npm:用来安装前端开发中用到的包,是安装Node.js时自动安装的。
i(install):表示安装。
-g(–global):全局标识,可以在任意目录中使用该工具。

  1. 安装查看: tsc -v / tsc --version



安装简化执行TS的工具包 ts-node

  • 这个工具包内部会先用tsc命令, 把TS文件转为JS文件
  • 内部再使用命令ts-node,用来执行生成的JS代码。

安装命令:npm i -g ts-node

使用方式:ts-node hello.ts

  • 后期在脚手架中使用ts就不会这么麻烦, 初学语法暂时使用这个方式



入门代码

创建 hello.ts 文件,console.log(“hello TS”)

在终端进行执行:tsc hello.ts

会生成一个 hello.js 文件



TS中的注释

// 单行

/* 多行 */



JS原有类型

  • 限定了数据类型后,后续的赋值必须符合属性类型,否则编译报错!
  • 基础类型如果首字母大写, 赋值的时候可以采用实例化的形式



number

数值类型- number(整数和浮点数)

除了支持十进制和十六进制字面量,TypeScript还支持ECMAScript 2015中引入的二进制和八进制字面量。

let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;



string

  • string 单引号(推荐)或双引号或``都可以
let name: string = "bob";
name = "smith";



boolean

  • 布尔值 boolean true/false值
let isDone: boolean = false;



undefined

  • 表示此处应该有值, 但是现在没有值
  • 申明了一个变量, 但是未赋值, 这个变量就是undefined
  • 在TS中, 可以给已有类型的基本数据类型变量, 重新赋值undefined
let u:undefined = undefined



null

  • 表示此处不应该有值, 没有值了
  • 在TS中, 可以给已有类型的引用数据类型变量, 重新赋值null
let n: null = null 



object

对象类型基本使用

  • js中属于对象的太多,添加object后等于是没有添加类型限制,开发很少用

  • 示例1:

    // {}也表示对象,表示变量o必须是一个对象类型,在其中可以指定属性类型
    // 以下示例表示对象o中只能有指定的属性名称和类型,以及个数
    // 在属性名后面加?,表示该属性可选
    let o: {
    	name: string, 
    	age: number,
    	sex?: string
    };
    
    o = {name: '孙悟空',age: 18} // ok
    o = {name: '孙悟空'} // 报错,少了一个age属性
    o = {name: '孙悟空',age: 18,sex: '男'} // ok
    

    示例2:

    // 需求:在一个对象中,我只有一个属性是有要求的,其他的属性名称、类型、个数都未知
    let o: {
    	name: string, 
    	[propertyName: string]: any
    }
    
    /*
    * 以上代码,[propertyName: string] 表示属性名是string(对象的属性名都是字符串类型)
    * [propertyName: string]: any 表示该对象中的属性是任意类型,
    *						如果any是string,表示该对象中的属性都必须是string类型
    */
    

    实例3:

    function getObj(obj: object):object {  
    	// do something  
    	return {    
    		name:"卡卡西"  
    	}
    }
    
    console.log(getObj({name:"佐助"}))
    console.log(getObj(new String('字符串')))
    



对象类型中的函数写法

指定对象中, 函数的类型

  1. 写法一: sayHi(name:string):void

    const user: {  
    	name:string  
    	sayHi(name:string):void
    }  
    
    user = {  
    	name:'张三'  
    	sayHi(name) {    
    		console.log('你好,我叫' + name)  
    	}
    }
    
  2. 写法二: add(n1:number, n2:number) => number

    const user: {  
    	name:string  
    	add(n1:number, n2:number) => number
    }
    
    user = { 
    	name:'李四'  
    	add: function(a, b){    
    		return a+b  
    	}
    }
    



array

  • 数组; 开发中, 数组中的元素为同一类型

  • 语法1:类型[]

    // 表示数值数组
    let list: number[];
    let list: Array<number>;
    
    let list: number[] = [1, 2, 3];
    
  • 语法2:使用泛型写法,Array<元素类型>

    // 表示字符串数组
    let list: string[];
    let list: Array<string>    
    
    let list: Array<number> = ['a', 'b', 'c'];
    

扩展: 申明二维数组

let arr:number[][] = [
	[1,2,3],[7,8,9]
]



function

函数类型的定义

  • 函数主要是限制出入参的类型及个数, 并无固定的针对性语法, 实现方式多种
  1. 单独限制出入参类型

    • 箭头函数的写法常用
    // 箭头函数
    let addFunc = (num1:number, num2:number):number => {  
    	return num1 + num2
    }
    	
    addFunc = function(n1, n2): number{    
    	return n1 + n2; 
    }
    console.log(addFunc(2, 6)) // 8
    
    
    ***************************************************
    
    
    // 函数声明
    function add(num1:number, num2:number):number {  
    	return num1 + num2
    }
    console.log(add(2,2)) // 4
    
  2. 使用类型别名

    type AddFun = (num1:number, num2:number):number
    
    const add:AddFun = (num1, num2) => {  
    	return num1 + num2
    }
    
    • 注意: 只适合函数表达式

      // 函数表达式
      let fun1 = function(){}
      
      // 函数声明
      function fun2(){}
      
  3. 使用接口

    • 为了使用接口表示函数类型,需要给接口定义一个调用签名
    interface ISearchFunc {    
    	// 定义调用签名    
    	(source:string, subString:string): boolean
    }
    
    // 定义一个函数,该类型即为上面的接口
    const searchString: ISearchFunc = function (source:string, subString:string): boolean {    
    	return source.search(subString) > -1
    	}
    	
    // 调用
    console.log(searchString('道阻且长,行则将至','长')) // true
    



扩展 - 函数的参数

默认值

function(name: string, age: number = 18){

}



可选参数

  • 添加 ?,接口也适用(表示属性可省略)
  • 在入参变量名后面添加 ? , 注意可选参数必须放在最后, 可以是一个或多个可选参数
  • 函数体中对可选参数需使用非空校验相关逻辑
// 表示入参age, sex,可省略
function(name: string, age?: number, sex?: string){

}



TS新增类型

any

  • 任意类型(any可略) , 尽量不要使用any

  • 设置为该类型后等于对变量关闭了TS的类型检测, 直接让它们通过编译阶段的检查

  • 可以赋值给任意类型变量

  • // eg1:多次赋值均不报错
    let a: any = 123
    a = "str"a = true
    
    // eg1:它可以赋值给其他任意类型,使其他类型也称为any类型
    // 简单理解就是b是any类型,经过下面赋值,使a也成了any类型
    let b:number = 123
    b = a
    



unknown

  • 未知类型的值; 编码中,尽量使用unknown代替any
  • 和any的异同点

    1. 同:都是可以多次赋值不同类型的值
    2. 异:any可以赋值给任意任意类型的值,使其也称为any的值,但是unknown不能直接赋值其他类型的值
  • 一般来说,这个类型并不是开发者手写的,是网络传来的,需要配和断言使用(在使用的时候需要明确这个变量的类型,可以多次指定类型)

    type A = {name:string}
    type B = {age:number}
    // 模拟ajax传递过来的数据
    let c: unknown = JSON.parse("{'name':"Tom"}") 
    
    let var1 = (c as A).name
    let  var2 = (c as B).age
    



void

  • 空值 , 用于函数返回值 表示函数没有返回值(常用)
  • 实际编码中,其实可以return null或者return undefined,但是没有意义,应该是语法上的兼容而已
function fn(): void{     
	// do something     
	return; // 或者不写return
}



never

  • 永不存在的值的类型
  • 开发中使用较少,一般用于抛出异常、无限循环的函数返回类型
  • 出现该类型的时候,注意检查代码是否有问题

语法:

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {    
	throw new Error(message);
}

// 推断的返回值类型为never
function fail() {    
	return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {    
	while (true) {
	}
}

eg:

type Code = 1|2|3|undefined
let dir:Code // 表示dir的取值只能是”1,2,3,undefined“ 四者之一
switch(dir){    
	case 1:        
		break;    
	case 2:        
		break;    
	case 3:        
		break;    
	case undefined:      
		break;    
	default:        
		console.log('如果进入该分支,表示dir的值不在”1,2,3,undefined“中, 即为never类型')}



元组

  • 固定长度的数组(元素类型可以不一致)

  • 结合数组理解, 最大的区别的是元组的长度固定, 且每个索引对应的元素类型固定

  • 对于值, 可以get, set, update, 注意变更的值只能是规定的类型, update可以用arr[0] = newValue, set可以用arr.push(newValue)

    // 表示数组中只能有2个string的元素,长度不可变
    let arr1: [string, string]
    
    let arr2: [number, string] 
    



enum

  • 枚举; 默认枚举值有编号,类似数组索引,从0开始

eg1:

enum Gender {     
	Male,     
	Female
}

let tom: Gender = Gender.Male
console.log(tom)  // 0
console.log(Gender.Male, Gender.Female) // 0 1
  • 特殊使用方式:

如果枚举值是数字值, 可以用以下写法

enum Color {  
	Red = 6,  
	Green = 7,  
	Blue = 8
}

let c:string = Color[6]  // Red



联合类型

  • 把多个类型联合为一个类型 (表示取值可以为多种类型中的一种)
  • | 隔开
// 表示入参param可以是number或者string类型
// 出参为string类型
function getSomeThing(param: number|string):string {    
	return param+''
}

getSomeThing(123)
getSomeThing("字符串")
type MyType = 1 | 2 | 3 | 4 | 5;

let a: MyType = 2;
let b: MyType = 4;
// 数组中,既可以有字符串,也可以有数字
let arr:(number|string)[]

arr = [123, '哈哈']a
rr = [66, 88, 99]



typeof 操作符

用来获取一个变量或对象的类型

interface Person {  
	name: string;  
	age: number;
}
const test: Person = { name: "Tom", age: 20 };
type Sys = typeof test; // type Sys = Person

在上面代码中,我们通过 typeof 操作符获取 test 变量的类型并赋值给 Sys 类型变量,之后我们就可以使用 Sem 类型:

const Vivo: Sys  = { name: "Lili", age: 3 }

也可以对嵌套对象执行相同的操作:

const kakaxi = {    
	name: "kakaxi",    
	age: 27,    
	address: {      
		province: '湖南',      
		city: '长沙'       
	}
}
type Kakaxi = typeof kakaxi;
/*	以下即为Kakaxi类型 
	type Kakaxi = {    
		name: string;    
		age: number;    
		address: {        
			province: string;        
			city: string;    
		};
	}
*/

此外, typeof 操作符除了可以获取对象的结构类型之外,它也可以用来获取函数对象的类型,比如

function toArray(x: number): Array<number> {  
	return [x];
}
type Func = typeof toArray; // -> (x: number) => number[]



类型断言

断言语法

  • 同java中的类型转换
  • 关于断言是用来跳过编译检查的语法
  • 主要用于当 TypeScript 推断出来类型并不满足你的需求,你需要手动指定一个类型。

  • 只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。

有两种语法,关键字 as 和标签 <> 两种,

  • <类型>值
  • 值 as 类型

由于<> 会与JSX 语法冲突,建议统一使用 as 来进行类型断言

as语法(推荐):

let someValue: any = "this is a string";

let strLength: number = (someValue as string).length;

<> 语法:

let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;



断言扩展

非空断言 !

  • ! 表示此处一定有值, 跳过类型检查

如果编译器不能够去除 null 或 undefined,可以使用非空断言 ! 手动去除。

function fixed(name: string | null): string {  
	function postfix(epithet: string) {    
		return name!.charAt(0) + '.  the ' + epithet; // name 被断言为非空  
	}  
	name = name || "Bob"  
	return postfix("great")
}

代码解释:

第 2 行,postfix() 是一个嵌套函数,因为编译器无法去除嵌套函数的 null (除非是立即调用的函数表达式),所以 TypeScript 推断第 3 行的 name 可能为空。

第 5 行,而 name = name || "Bob" 这行代码已经明确了 name 不为空,所以可以直接给 name 断言为非空(第 3 行)。



双重断言

双重断言极少有应用场景,只需要知道有这种操作即可:

interface User {  
	nickname: string,  
	admin: boolean,  
	group: number[]
}

const user = 'Evan' as any as User

代码解释: 最后一行,使用 as 关键字进行了两次断言,最终变量 user 被强制转化为 User 类型。



扩展 - 类型的其他用法

类型别名

  • 为任意类型取别名, 语法: type 别名 = 类型
  • 使用场景: 当同一类型(复杂)被多次使用时, 可以通过类型别名, 简化对该类型的编码
  • 需要使用关键字 type, 别名命名, 建议首字母大写
type CustomArray = (number|string)[]

let arr1:CustomArray = [1, '啊', 6]
let arr2:CustomArray = ['x', 'y', 8]



一个变量指定多个类型

// 表示a可以是string或number类型
let a: string | number;

扩展&使用

let j:{name: string} & {age: number}

j = {	
	name: '卡卡西',  
	age: 25
}

j = {name: '鸣人'} // 报错,删了一个age属性



继承、实现概述

extends

  • 调用父类使用super
  • 子类继承父类的属性和方法
  • 子类可以改写父类的属性和方法



类的继承

只能单继承, 但是可以多级继承

class A {    
	name:string;    
	constructor(name:string){        
		this.name = name    
	}
}
class B extends A {    
	age:number;    
	constructor(age:number, name:string){        
		// 必须先用super调用父类构造器        
		super(name)        
		this.age = age    
	}
}
class C extends B {     
	sex:string;    
	constructor(sex:string, age:number, name:string){        
		super(age,name)        
		this.sex = sex    
	}
}
const instance = new C('张三', 18, '男')
console.log(instance)
// C { name: '男', age: 18, sex: '张三' }



接口的继承

接口可以多继承

interface A {    
	id: number
}
interface B {    
	code: string
}
interface C extends A,B {    
	isFlag: boolean
}

const test:C = {    
	id:200,    
	code:'凌凌漆',    
	isFlag:false
}

console.log(test)
// { id: 200, code: '凌凌漆', isFlag: false }



implements

  • 实现是对接口、抽象类中规定的方法, 进行具体的编码
  • 实现可以多实现



类、接口、抽象类

在TS中, 这三者的概念, 和Java中一样, 这里只用最直白简洁的语言进行阐述.



  • 类最单纯的理解可以看做是代码的封装, 好比数组是储存数据, 而类可以存储的更多, 还可以存储代码



接口

  • 简单理解成一种约束、规则.
  • 主要通过定义抽象方法, 让继承的子类去实现
  • 试着理解如果引入一个插件, 需要创建一个类, 写固定的方法进行配置, 在插件里就可以通过定义接口, 让用户实现该接口, 进而约束用户进行固定配置的编码

##抽象类

  • 开发不常用

  • 里面既可以有具体方法, 也可以有抽象方法

  • 既是对子类, 公共代码的封装; 也是对子类, 需要具体编码的方法的约束

  • 可以结合上述两者的功能, 对其理解



接口及接口中属性的扩展

接口

  • 类比java,可看做一种强制性的规范,一种约束
interface IPerson {    
	firstName:string    
	lastName:string
}

function showFullName (person:IPerson) {    
	return `${person.firstName}_${person.lastName}`
}

let kakaxi:IPerson {	
	firstName:'旗木'  
	lastName:'卡卡西'
}

console.log(showFullName(kakaxi))
// 旗木_卡卡西



接口继承

  • 和类的继承是一个概念

  • TS支持继承多个接口

    interface point2D {  
    	x: number   
    	y: Number }
    interface point3D {    
    	z: number 
    }
    interface point4D extends point2D, point3D {   
    	w: number
    }
    
    const pointTest: point4D = {    
    	x: 100, 
    	y: 200, 
    	z: 300, 
    	w: 400
    }
    console.log(pointTest)
    



扩展 - 只读和可省略

  • readonly 只读
  • ? 可省略
interface IAnimals {    
	// id是只读的number类型    
	readonly id:number    
	name:string    
	age:number    
	// sex可省略    
	sex?:string
}

const dog:IAnimals = {    
	id: 1,    
	name: '来福',    
	age: 2,    
	sex: '男'
}



类、继承、super

类的概念

  • 类比Java
  • 类的类型,通过接口定义
  • 一个类可以同时实现多个接口
  • 类里面定义构造函数contructor,是为了实例化的时候对属性值进行初始化
  • 接口和接口之间:继承
  • 类和接口之间:实现
interface IFly {    
	fly:()
}

// 定义一个类,这个类的类型就是上面定义的接口
class Person implements IFly {    
	fly() {        
		console.log("飞上天,和太阳肩并肩")    
	}
}

const p1 = new Person()
p1.fly()
interface IFly {    
	fly:()
}
interface ISwim {    
	swim:()
}

class Person implements IFly,ISwim {    
	fly() {        
		console.log("会飞天")    
	},     
	swim() {        
		console.log("会游泳")    
	}
}

const p2 = new Person()
p2.fly()
p2.swim()
interface TMyFlyAndSwim extends IFly,ISwim {}
class Person implements TMyFlyAndSwim {    
	fly() {        
		console.log("会飞天")    
	},     
	swim() {        
		console.log("会游泳")    
	}
}

const p3 = new Person()
p2.fly()
p2.swim()



继承

  • 同比Java

  • super关键字可以调用父类的构造器,也可以调用父类中的实例函数

    class Person {    
    	name:string    
    	age:number    
    	gender:string    
    	sayHi(food:string){        
    		console.log(`我是${this.name},喜欢${food}`)   
    	 }    
    	 constructor(name:string,age:number,gender:string) {       
    	 	this.name = name        
    	 	this.age = age        
    	 	this.gender = gender    
    	 }
    }
    
    class Student extends Person {   
    	constructor(
    		name:string,
    		age:number,
    		gender:string) {        
    			// 使用super,调用父类的构造器实例化        
    			super(name,age,gender) { 
    		}    
    		// 调用父类中的方法    
    		sayHi(food:string) {       
    			super.sayHi(food)    
    		}
    }
    
    const p1 = new Person("张三",25,"男")
    p1.sayHi('西瓜') // 我是张三,喜欢西瓜
    
    const s1 = new Student('小明',18,'男)
    s1.sayHi('草莓') // 我是小明,喜欢草莓
    



多态

  • 父类引用,指向子类对象
  • 不同类型的对象,针对相同的方法,产生了不同的行为
class Animal {    
	name:string    
	constructor(name:string){		
		this.name = name           
	}    
	run(distance:number) {        
		console.log(`${this,name}跑了${distance}米远的距离`)    
	}
}

class Dog extends Animal {    
	constructor(name:string) {        
		super(name)    
	}    
	// 重写run函数    
	run(distance:number) {        
		console.log(`${this,name}跑了${distance}米远的距离-----dog`)    	
	}
}

class Pig extends Animal {    
	constructor(name:string) {        
		super(name)    
	}    
	// 重写run函数    
	run(distance:number) {        
		console.log(`${this,name}跑了${distance}米远的距离======pig`)    
	}
}

const ani:Animal = new Animal('动物')
ani.run()

const dog:Animal = new Dog('旺财')
dog.run()

const pig:Animal = new Pig('佩奇')
pig.run()

// 使用多态
const dog1:Animal = new Dog('小狗')
dog1.run()
const pig1:Animal = new Dog('小猪')
pig1.run()



修饰符

用于描述类中的成员(属性、构造器、函数)的可访问性

  • public: 默认;表示公共的,被修饰的成员,任何位置都可访问;
  • private:表示私有的,被修饰的成员,仅类中可访问;子类的类中也不能访问(this.xxx);
  • protected:表示受保护的,被修饰的成员,子类的类中可访问(this.xxx);外部不可访问;



泛型

基本概念

泛型就是解决 类、接口、方法的复用性,以及对不特定数据类型的支持

  • 函数名称后加<>, 一般使用符号 T 占位, T代表具体的类型
  • 入参、出参的类型一般具有关联性
function identity<K>(...arg: T[]): T[] {    
	return [...arg]
}
console.log(identity(1,2,3))



keyof关键字

keyof 用于获取某种类型的所有键,其返回类型是联合类型

代码释义:

表示只能是传入OOO类型, OOO类型中的属性之一(“id” | “code” | “age”)

let obj = {    
	id:"100",    
	code:996,    
	age:18
}
type OOO = typeof objfunction main<T, P1 extends keyof T>(obj:T, prop1: P1) {  
	console.log(obj[prop1]);
}

main(obj, 'id');   // '100'
main(obj, 'code'); // 996
main(obj, 'age');  // 18



泛型约束

泛型可以表示任意类型, 但是实际编码中, 有时候可以知道类型大致的范围,

为了避免使用出错, 给泛型限定类型的使用范围, 这就是泛型约束(缩小类型的取值范围)

  • 一般通过两种方式实现
    1. 指定更加具体的类型
    2. 添加约束

eg1: 限制出入参为数组, 数组元素类型不固定

function func<T>(param: T[]): T[] {  
	return param
}

func([1,2,3,4])

eg2: 传入的类型, 必须含有 length 属性

interface ILength {    
	length: number
}
function getId<T extends ILength>(param: T): T {    	
	console.log(param.length)    
	return param
}

// 虽然字符串和数组并没有继承 ILength, 但自身拥有length属性
console.log(getId('农夫山泉'));
console.log(getId([11,33,44]));

eg2: 传入的类型是一个对象, 必须拥有name, age属性

interface IUser {  
	name: string  
	age: number
}

function func<T extends IUser>(param: T): T {  	
	console.log(param.name)    
	console.log(param.age)    
	return param
}



泛型在接口中的使用

interface IUser<T> {  
	getId: (param: T) => T
}



泛型工具类

  • 泛型工具类型:TS 内置了一些常用的工具类型,来简化 TS 中的一些常见操作
  • 说明:它们都是基于泛型实现的(泛型适用于多种类型,更加通用),并且是内置的,可以直接在代码中使用。 这些工具类型有很多,主要学习以下几个:
  1. Partial<Type>
  2. Readonly<Type>
  3. Pick<Type, Keys>



Partial

  • Partial 用来构造(创建)一个类型,将 Type 的所有属性设置为可选。
type Props =  {  
	id: string  
	children: number[]
}

type PartialProps = Partial<Props>
  • 解释:构造出来的新类型 PartialProps 结构和 Props 相同,但所有属性都变为可选的。



Readonly

  • Readonly 用来构造一个类型,将 Type 的所有属性都设置为 readonly(只读)。
type Props =  {  
	id: string  
	children: number[]
}

type ReadonlyProps = Readonly<Props>
  • 解释:构造出来的新类型 ReadonlyProps 结构和 Props 相同,但所有属性都变为只读的。
let props: ReadonlyProps = { 
	id: '1', 
	children: [] 
}
// 错误演示
props.id = '2'
  • 当我们想重新给 id 属性赋值时,就会报错:无法分配到 “id” ,因为它是只读属性。



Pick

  • Pick<Type, Keys> 从 Type 中选择一组属性来构造新类型。
interface Props {  
	id: string  
	title: string  
	children: number[]
}
type PickProps = Pick<Props, 'id' | 'title'>
  • 解释:
    1. Pick 工具类型有两个类型变量:1 表示选择谁的属性 2 表示选择哪几个属性。 2. 其中第二个类型变量,如果只选择一个则只传入该属性名即可。
    2. 第二个类型变量传入的属性只能是第一个类型变量中存在的属性。
    3. 构造出来的新类型 PickProps,只有 id 和 title 两个属性类型。
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值