TypeScript

本文章基于黑马程序员笔记来编写

目录

TypeScript 的介绍

 TypeScript 为什么要为 JS 添加类型支持?

TypeScript 相比 JS 的优势

TypeScript 初体验

安装编译 TS 的工具包

编译并运行 TS 代码

简化运行 TS 的步骤

TypeScript 常用类型

1. 类型注解

2. 常用基础类型

 1. JS 已有类型

原始类型

对象类型

2. TS 新增类型

联合类型:

类型别名(自定义类型):

函数类型:

对象类型

接口

继承

元组

类型推论

类型断言

字面量类型 

枚举

any 类型

typeof 类型


TypeScript 的介绍

标题

T ype S cript (简称:TS)是 JavaScript 的 超集 (JS 有的 TS 都有)。
TypeScript = Type + JavaScript(在 JS 基础之上,为 JS 添加了 类型支持 )。
TypeScript 是微软开发的开源编程语言,可以在任何运行 JavaScript 的地方运行。

 TypeScript 为什么要为 JS 添加类型支持?

背景:JS 的类型系统存在“先天缺陷”,JS 代码中绝大部分错误都是 类型 错误(Uncaught Type Error)。
问题:增加了找 Bug、改 Bug 的时间,严重影响开发效率。
从编程语言的动静来区分,TypeScript 属于静态类型的编程语言,JS 属于动态类型的编程语言。
静态类型:编译期做类型检查; 动态类型:执行期做类型检查。
代码编译和代码执行的顺序:1 编译 2 执行。
对于 JS 来说:需要等到代码真正去 执行 的时候才能 发现错误 (晚)。
对于 TS 来说:在代码 编译 的时候(代码执行前)就可以 发现错误 (早)。
并且,配合开发工具,TS 可以 提前到在编写代码的同时 就发现代码中的错误, 减少找 Bug、改 Bug 时间

TypeScript 相比 JS 的优势

1. 更早(写代码的同时)发现错误, 减少找 Bug、改 Bug 时间 ,提升开发效率。
2. 程序中任何位置的代码都有 代码提示 ,随时随地的安全感,增强了开发体验。
3. 强大的 类型系统 提升了代码的可维护性,使得 重构代码更加容易
4. 支持 最新的 ECMAScript 语法 ,优先体验最新的语法。
5. TS 类型推断 机制, 不需要 在代码中的 每个地方都显示标注类型 ,尽量降低了成本。
除此之外,Vue 3 源码使用 TS 重写、Angular 默认支持 TS、React 与 TS 完美配合。

TypeScript 初体验

安装编译 TS 的工具包

问题:为什么要安装编译 TS 的工具包?
回答:Node.js/浏览器,只认识 JS 代码,不认识 TS 代码。需要先将 TS 代码转化为 JS 代码,然后才能运行。

安装命令:npm i -g typescript。
typescript 包:用来编译 TS 代码的包,提供了 tsc 命令,实现了 TS -> JS 的转化。
验证是否安装成功:tsc –v(查看 typescript 的版本)。

TS的执行过程:

编译并运行 TS 代码

1. 创建 hello .ts 文件(注意:TS 文件的后缀名为 .ts )。

写入:

let str : number = 1245;
console.log(str);

2. 将 TS 编译为 JS:在终端中输入命令, tsc hello.ts
可以用  tab  键补齐
tsc 01.hello.ts

执行完 上面的命令 会在同级目录中会出现一个同名的 JS 文件

3. 执行 JS 代码:在终端中输入命令,node hello.js。
node 01.hello.js 

执行结果为:  12345

简化运行 TS 的步骤

问题描述:每次修改代码后,都要 重复执行 两个命令,才能运行 TS 代码,太繁琐。
简化方式:使用 ts-node 包,直接在 Node.js 中执行 TS 代码。
安装命令:npm i -g ts-node (ts-node 包提供了 ts-node 命令)。

npm i -g ts-node

使用方式:ts-node hello.ts。

ts-node hello.ts

解释:ts-node 命令在内部将 TS -> JS,然后,再运行 JS 代码。 

TypeScript 常用类型

TypeScript 是 JS 的超集,TS 提供了 JS 的所有功能,并且额外的增加了: 类型系统
类型系统 分别为 1. 类型注解     2. 常用基础类型
1. 所有的 JS 代码都是 TS 代码。
2. JS 有类型(比如,number/string 等)但是: JS 不会检查变量的类型是否发生变化 。而 TS 会检查
TypeScript 类型系统的主要优势:可以 显示标记出代码中的意外行为 ,从而降低了发生错误的可能性。

1. 类型注解

示例代码:
let age : number = '20';

// 说明:代码中的 : number 就是类型注解。
// 作用:为变量添加类型约束。比如,上述代码中,约定变量 age 的类型为 number(数值类型)。
// 解释:约定了什么类型,就只能给变量赋值该类型的值,否则,就会报错。

2. 常用基础类型

可以将 TS 中的常用基础类型细分为两类:1 JS 已有类型。  2 TS 新增类型。
1. JS 已有类型
原始类型 number/string/boolean/null/undefined/symbol
对象类型 object (包括,数组、对象、函数等的对象)。
2. TS 新增类型
联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any 等。

 1. JS 已有类型

原始类型

1. 原始类型:number/string/boolean/null/undefined/symbol。
特点: 简单 。这些类型,完全按照 JS 中类型的名称来书写。

let MyName : string = '无名氏';

let age : number = 20;

let isLoading :boolean  = false;

let a : null = null;

let b : undefined = undefined;

let sy : symbol = Symbol();

对象类型

2. 对象类型:object(包括,数组、对象、函数等对象)。
特点:对象类型,在 TS 中更加细化, 每个具体的对象都有自己的类型语法
数组类型 的两种写法:( 推荐使用 number[] 写法

// 1.数据类型

//写法一:
let num : number[] = [1,2,3,4];

//写法二:
let str : Array<string> = ['a','b','c'];

2. TS 新增类型

例如:数组中既有 number 类型,又有 string 类型,这个数组的类型应该如何写?

联合类型:

// 联合类型:
// 添加小括号,表示:首先是数组,然后,这个数组中能够出现 number 或 string 类型的元素

let arr : (string | number)[] = ['a', 1 ,'b', 2];
解释:
| (竖线)在 TS 中叫做 联合类型 (由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种)。
扩展:
没有小括号的  表示的是只能为 其中的一种数据类型

let arr1 : number | string[]  = ['1','2','3'];

let arr2 :  string |  number  = 123;

注意:这是 TS 中联合类型的语法,只有一根竖线,不要与 JS 中的或(||)混淆了。

类型别名(自定义类型

类型别名 (自定义类型):为任意类型起别名。
使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名, 简化该类型的使用
type CustomArray = (number | string)[];

let arr3 : CustomArray = [1,'a',2,'b',3];

let arr4 : CustomArray = ['x',1,'y',2];

解释:
1. 使用 type 关键字来创建类型别名。
2. 类型别名(比如,此处的 CustomArray),可以是任意合法的变量名称。
3. 创建类型别名后,直接 使用该类型别名作为变量的类型注解 即可。

函数类型:

函数的类型实际上指的是:函数 参数 返回值 的类型。
为函数指定类型的两种方式:1. 单独指定参数、返回值的类型 。2 .同时指定参数、返回值的类型。
1. 单独指定参数、返回值的类型:
3. 函数类型

1 单独指定参数、返回值的类型 

// ():number 表示 返回值类型为 number类型

// () => number 表示 返回值类型为 number类型

// 方法一
function add(num1 : number , num2 : number) : number{
  return num1 + num2 ;
}

// console.log(add(1,2));

// 方法二
const add2 = (num1 : number , num2 : number) : number => {
  return num1 * num2 ;
}

// console.log(add2(1,2));

2. 同时指定参数、返回值的类型:
//2. 同时指定参数、返回值的类型。


const add3 :  (num1 : number , num2 : number) => number = (num1 , num2) => {
  return num1 * num2;
}
console.log(add3(1,2));


//结合联合类型
const add4 :  (num1 : number , num2 : string) => (number | string) = (num1 , num2) => {
  return num1 + num2;
}
console.log(add4(1,'2')); //number 和 string 相加是连接 12

解释:当函数作为表达式时,可以通过 类似箭头函数形式的语法 来为函数添加类型。
注意:这种形式只适用于函数表达式。
扩展:
 

3.同时指定参数无返回值的类型

如果函数没有返回值,那么,函数返回值类型为:void。

 void类型 是 ts新增的 js中没有的

function greet(name : string) : void {
  console.log('Hello' + name);
}

greet("Zys");//HelloZys

4. 同时指定 可选参数 、返回值的类型:

使用函数实现某个功能时,参数可以传也可以不传。

这种情况下,在给函数参数指定类型时,就用到可选参数了  可选参数符号 变量名后加 ? 英文问号,  写法为 :    xx   。

比如,数组的 slice 方法,可以 slice() 也可以 slice(1) 还可以 slice(1, 3)。

//当第一个是 必选参数, 第二个是 可选参数, 调用函数时第一参数必须传参

function mySlice(start : number, end? : number) : void{
  console.log('起始索引' + start, '结束索引' + end);
}

mySlice(1,2);//起始索引 1 结束索引 2

//当第一个是 可选参数,  第二个是 必选参数,

//这时 第二个必选参数 会报错, 因为 必选参数不能位于可选参数后(可选参数后不能添加必选参数)。

function mySlice2(start? : number, end : number) : void{
    console.log('起始索引' + start, '结束索引' + end);
}

//mySlice2() 函数报错。

此时的参数  end 会报错提醒 :

可选参数:在可传可不传的参数名称后面添加 ? (问号)。
注意: 可选参数只能出现在参数列表的最后 ,也就是说可选参数后面不能再出现必选参数。

对象类型

1..对象的属性和方法
JS 中的对象是由属性和方法构成的,而 TS 中 对象的类型 就是在 描述对象的结构 (有什么类型的属性和方法)。
对象类型的写法:
//对象类型的写法:

let person : { name: string; age: number; sayHi(): void; greet(name: string): void} = {
  name : 'java老师',
  age : 20,
  sayHi() {},
  greet(name) {}
}

console.log(person.name);//java老师


let person2 : { 
  //没在同一行中书写  可以省略分号 ;
    name: string
    age: number
    //sayHi(): void
    //利用箭头函数书写
    sayHi : () => void
     greet(name: string): void
    } = {
  name : 'web老师',
  age : 21,
  sayHi() {},
  greet(name) {}
}

console.log(person2.age); //21

解释:
1. 直接使用 {} 来描述对象结构。属性采用 属性名: 类型 的形式;方法采用 方法名(): 返回值类型 的形式。
2. 如果方法有参数,就在方法名后面的小括号中指定参数类型(比如: greet(name: string): void )。
3. 在一行代码中指定对象的多个属性类型时,使用 ;(分号)来分隔。
如果一行代码只指定一个属性类型(通过换行来分隔多个属性类型),可以去掉 ;(分号)。
方法的类型也可以使用箭头函数形式(比如:{ sayHi: () => void })。
2.对象的可选属性和方法
对象的属性或方法,也可以是可选的,此时就用到 可选属性 了。
比如,我们在使用 axios({ … }) 时,如果发送 GET 请求,method 属性就可以省略。
可选属性 的语法与函数可选参数的语法一致,都使用 ? (问号)来表示。
function myAxios(config: { url: string; method?: string}){
  console.log(config); //{ url: 'https:xxx' }
}

//调用 myAxios 函数

myAxios({
  url : 'https:xxx',
  //method 是可选属性
})

接口

当一个对象类型被多次使用时,一般会使用 接口 interface )来描述对象的类型,达到 复用 的目的。
解释:
1. 使用 interface 关键字来声明接口。
2. 接口名称(比如,此处的 IPerson),可以是任意合法的变量名称。
3. 声明接口后,直接 使用接口名称作为变量的类型
4. 因为每一行只有一个属性类型,因此,属性类型后没有 ;(分号)
//接口书写:

interface IPerson  {
  name : string;
  age : number;
  sayHi() : void;
  //可选类型
  mmm ?: number
} 

//复用 IPerson

let person3 : IPerson = {
  name : 'c#老师',
  age : 22,
  sayHi() {}
}

let person4 : IPerson = {
  name : 'python老师',
  age : 23,
  sayHi() {},
  mmm : 12345
}

扩展:
interface(接口)和 type(类型别名)的对比:
相同点:都可以给对象指定类型。
不同点:
接口,只能为对象指定类型。
类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名。
//interface(接口)书写 interface xxx {}

interface IPerson2  {
  name : string;
  age : number;
  sayHi() : void;
  //可选类型
  mmm ?: number
} 
//使用接口
let person5 : IPerson2 = {
  name : 'IPerson2',
  age : 22,
  sayHi() {}
}

//type (类型别名)
//type (类型别名) 书写  type xxx = {}

type IPerson3 = {
  name: string
  age: number
  sayHi() : void
}

//使用type
let person6 : IPerson3 = {
  name : 'IPerson3',
  age : 22,
  sayHi() {}
}

继承

如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用。

比如,这两个接口都有 x、y 两个属性,重复写两次,可以,但很繁琐。

interface Point2D { x: number; y: number }

interface Point3D { x: number; y: number; z : number}

//使用 继承 来复用

interface Point2D { x: number; y: number }
interface Point3D extends Point2D{
  // x y 都继承与 Point2D
  //Point3D 自己的属性
   z : number 
  }

//调用 Point3D

let p1 : Point3D = {
  x : 1,
  y : 2,
  z : 3

}
console.log(p1.x + p1.y + p1.z); // 相加=6
解释:
1. 使用 extends (继承)关键字实现了接口 Point3D 继承 Point2D。
2. 继承后,Point3D 就有了 Point2D 的所有属性和方法(此时,Point3D 同时有 x、y、z 三个属性)。

元组

元组类型是另一种类型的数组,它 确切地知道包含多少个元素,以及特定索引对应的类型
 let position2 : [number,number] = [ 10.2321,  25.1534 ] 

//两个number元素 两个元素  0索引 1索引...

 let position3 : [number,string] = [ 10.2321,  '25.1534' ] 

//一个number类型和一个string类型  两个元素   0索引 1索引...

解释:
1. 元组类型可以确切地标记出有多少个元素,以及每个元素的类型。
2. 该示例中,元素有两个元素,每个元素的类型都是 number。 
扩展:

// 扩展:什么时候用 元组呢?

// 确切地知道包含多少个元素,以及特定索引对应的类型。

// 写法 在 [] 中直接指定类型,多个用 逗号分开

场景:在地图中,使用经纬度坐标来标记位置信息。
可以使用数组来记录坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型。
使用 number[] 的缺点:不严谨,因为该类型的数组中可以出现任意多个数字。
更好的方式: 元组 Tuple )。

类型推论

在 TS 中,某些没有明确指出类型的地方,TS 的 类型推论机制会帮助提供类型
换句话说:由于类型推论的存在,这些地方,类型注解可以 省略 不写!
发生类型推论的 2 种常见场景:
1. 声明变量并初始化时。

2. 决定函数返回值时。

代码示例:
//1 声明变量并初始化时

let age = 19;

//2 决定函数返回值时。

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

注意:这两种情况下,类型注解可以省略不写!
推荐: 能省略类型注解的地方就省略 (偷懒,充分利用TS类型推论的能力,提升开发效率)。
技巧:如果不知道类型,可以通过鼠标放在变量名称上,利用 VSCode 的提示来查看类型。

类型断言

1.类型断言
有时候你会比 TS 更加明确一个值的类型,此时,可以使用 类型断言 来指定更具体的类型。
比如:
<body>

  <!-- 写入a标签 -->
  <a href="https://www.baidu.com/" id="link">百度</a>

  <script>

    //获取id元素
    const aLink = document.getElementById('link');

  </script>
</body>

//aLink 类型为: HTMLElement

// id 类型存在

aLink?.id

//类型“HTMLElement”上不存在属性“href”。

//aLink.href

注意:getElementById 方法返回值的类型是 HTMLElement,该类型只包含所有标签公共的属性或方法,不包含 a 标签特有的 href 等属性。
因此,这个 类型太宽泛(不具体) ,无法操作 href 等 a 标签特有的属性或方法。
解决方式:这种情况下就需要 使用类型断言指定更加具体的类型
2.使用类型断言(指定该元素是什么类型)
/使用类型断言:

const aLink2 = document.getElementById('link') as HTMLAnchorElement;

// id 类型存在

aLink2.id

//aLink2 类型为:HTMLAnchorElement

aLink2.href

//aLink2 类型为: HTMLAnchorElement

解释:
1. 使用 as 关键字实现类型断言。
2. 关键字 as 后面的类型是一个更加具体的类型(HTMLAnchorElement 是 HTMLElement 的子类型)。
3. 通过类型断言,aLink 的类型变得更加具体,这样就可以访问 a 标签特有的属性或方法了。
3.类型断言另一种语法
另一种语法,使用 <> 语法,这种语法形式不常用知道即可:
//这种写法在react中与js语法冲突

const aLink3 = <HTMLAnchorElement>document.getElementById('link');
技巧:在浏览器控制台,通过 console.dir() 打印 DOM 元素,在属性列表的最后面,即可看到该元素的类型。

字面量类型 

思考以下代码,两个变量的类型分别是什么?
let str1 = 'Hello TS'

const str2 = 'Hello TS'
通过 TS 类型推论机制,可以得到答案:
1. 变量 str1 的类型为: string 。(let是变量  类型为 : string
2. 变量 str2 的类型为: 'Hello TS' 。(const是常量  类型为: "Hello TS"
解释:
1. str1 是一个变量(let),它的值可以是任意字符串,所以类型为: string
2. str2 是一个常量(const),它的值不能变化只能是 'Hello TS',所以,它的类型为: 'Hello TS'
注意:此处的 'Hello TS' ,就是一个 字面量类型 。也就是说 某个特定的字符串也可以作为 TS 中的类型
除字符串外,任意的 JS 字面量(比如,对象、数字等)都可以作为类型使用。
使用模式: 字面量类型配合联合类型一起使用
使用场景:用来 表示一组明确的可选值列表
比如,在贪吃蛇游戏中,游戏的方向的可选值只能是上、下、左、右中的任意一个。
function changeDirection(direction: 'up' | 'down' | 'left' | 'right'){
  console.log(direction)
}

//此时如果调用 changeDirection 方法, 只能选择四个值

changeDirection('right');

vscode会告诉我们 ,方法 changeDirection的可选值列表。

解释:参数 changeDirection 的值只能是 up/down/left/right 中的任意一个。
优势:相比于 string 类型,使用字面量类型更加精确、严谨。

枚举

1.枚举
枚举的功能类似于字面量类型+联合类型组合的功能,也可以 表示一组明确的可选值
枚举:定义一组命名常量 。它描述一个值,该值可以是这些命名常量中的一个。
// 写法: 枚举的关键字 enum 名字 { 可选值 }  如果有多个可选值 使用 逗号 分开

enum Direction { Up, Down, Left, Right}

function changeDirection(direction: Direction){
  console.log(direction)
}
解释:
1. 使用 enum 关键字定义枚举。
2. 约定枚举名称、枚举中的值以大写字母开头。
3. 枚举中的多个值之间通过 ,(逗号)分隔。
4. 定义好枚举后,直接使用枚举名称作为类型注解。
2.打印枚举

注意:形参 direction 的类型为枚举 Direction,那么,实参的值就应该是枚举 Direction 成员的任意一个。

访问枚举成员:

changeDirection(Direction.Up); //0  枚举的索引位

解释:类似于 JS 中的对象,直接通过点(.)语法访问枚举的成员。

3.数字枚举

枚举成员的值为数字的枚举,称为:数字枚举

问题:我们把枚举成员作为了函数的实参,它的值是什么呢?

// 解释:通过将鼠标移入 Direction.Up,可以看到枚举成员 Up 的值为 0。

// 注意:枚举成员是有值的,默认为:从 0 开始自增的数值。

// 当然,也可以给枚举中的成员初始化值

//比如:

//自定义索引位
enum Direction2 { Up = 10, Down = 20, Left = 1, Right = 39 }

//打印 Up 索引位为: 10
console.log(Direction2.Up); //10

4. 字符串枚举

字符串枚举:枚举成员的值是字符串。

enum Direction3 {
    Up = 'Up',
    Down = 'Down',
    Left = 'Left',
    Right = 'Right'
  }
注意:字符串枚举没有自增长行为,因此, 字符串枚举的每个成员必须有初始值
5.枚举的原理
枚举是 TS 为数不多的非 JavaScript 类型级扩展(不仅仅是类型)的特性之一。
因为:其他类型仅仅被当做类型,而 枚举不仅用作类型,还提供值 (枚举成员都是有值的)。
也就是说,其他的类型会在编译为 JS 代码时自动移除。但是, 枚举类型会被编译为 JS 代码
代码示例:

 TS

enum Direction {
  up = 'UP',
  down = 'DOWN',
  left = 'LEFT',
  right = 'RIGHT'
}

function changeDirection(Direction: Direction){
  console.log(Direction)
}

changeDirection(Direction.up);//UP

console.log(Direction) //{ up: 'UP', down: 'DOWN', left: 'LEFT', right: 'RIGHT' }

JS

var songs = ['枚举', '原理']

var Direction;
(function (Direction) {
  Direction['Up'] = 'UP'
  Direction['down'] = 'DOWN'
  Direction['left'] = 'LEFT'
  Direction['right'] = 'RIGHT'
})(Direction || (Direction = {}))

function changeDirection(Direction) {
  console.log(Direction)
}

changeDirection(Direction.Up) //UP

console.log(Direction) //{ Up: 'UP', down: 'DOWN', left: 'LEFT', right: 'RIGHT' }

说明:枚举与前面讲到的字面量类型+联合类型组合的功能类似,都用来表示一组明确的可选值列表。
一般情况下, 推荐使用字面量类型+联合类型组合的方式 ,因为相比枚举,这种方式更加直观、简洁、高效。

any 类型

原则: 不推荐使用 any 这会让 TypeScript 变为 “AnyScript”(失去 TS 类型保护的优势)。
因为当值的类型为 any 时,可以对该值进行任意操作,并且不会有代码提示。
//声明对象 obj
let obj: any = { x: 0 }

obj.bar = 100;

console.log(obj) //打印:{ x: 0, bar: 100 }
console.log(obj.bar) //在any类型下不报错 打印:100
console.log(obj.x) // 确实有这个属性 打印:0

const num : number = obj;

console.log(num); //在any类型下不报错  打印: { x: 0, bar: 100 }

解释:以上操作都不会有任何类型错误提示,即使可能存在错误!
尽可能的避免使用 any 类型,除非 临时使用 any 来“避免”书写很长、很复杂的类型!
其他隐式具有 any 类型的情况:
1. 声明变量不提供类型也不提供默认值。
2. 函数参数不加类型。
注意:因为不推荐使用 any,所以,这两种情况下都应该提供类型!

typeof 类型

众所周知,JS 中提供了 typeof 操作符,用来在 JS 中获取数据的类型。
console.log(typeof 'hello Ts') //string

实际上, TS 也提供了 typeof 操作符 :可以在 类型上下文 中引用变量或属性的类型(类型查询)。
这里的 类型上下文:指导是 函数中对象的属性 t
typeof xxx

t
使用场景: 根据已有变量的值,获取该值的类型,来简化类型书写。
//定义 变量p 方法
let p = {x: 1, y: '2'} //如果这里写的越复杂推荐使用typeof

//方法一:

function formatPoint(point : {x :number; y: string}){}
//调用方法,把变量p传给函数formatPoint
formatPoint({x : 1, y : '字符串1'})

//方法二: 简写

function formatPoint2(point: typeof p){}

formatPoint2( {x : 2, y : '字符串2'})

解释:
1. 使用 typeof 操作符来获取变量 p 的类型,结果与第一种(对象字面量形式的类型)相同。
2. typeof 出现在 类型注解的位置 (参数名称的冒号后面) 所处的环境就在类型上下文 (区别于 JS 代码)。
3. 注意:typeof 只能用来 查询变量或属性的类型 ,无法查询其他形式的类型(比如,函数调用的类型)。
比如:
//变量或属性的类型

let res = { x: 123 } 
let result: typeof res.x
//此时的 result 的类型是 number

//函数调用的类型

function add(num1 : number, num2 : number){
  return num1 + num2
}
let value: typeof add(1,2) //报错

此时typeof 只能用来查询变量或属性的类型。

如果查询函数调用的类型,则会报错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值