TypeScript(新手入门必备文章)

初识TS

npm install -g typescript 下载ts到全局
tsc -v 查看版本
同级创建tsconfig.json 文件 用于ts的配置

cmd中执行tsc hello.ts 可以创建出 js
ts代码报错也可以生成js文件

const hello = (name: string) => {
  return `hello${name}`;
};
hello("liusheng");
// hello(123); 会报错,编译也会报错

// tsc -w class.ts 监听变化,生成js文件
// npm install -g ts-node 一步运行
// ts-node class.ts

tsc --init 生成配置文件

数据类型

基础类型

  • boolean
  • number
  • string
  • null和undefined
/* 
原始数据类型
*/
// let isDone: boolean = 123;
let isDone: boolean = false;

let age: number = 13;
let binaryNumber: number = 0b1111; //es6  二进制和八进制

let firstName: string = "haohao";
let message: string = `Hello,${firstName},age is ${age}`;

let u: undefined = undefined;
let n: null = null;

// let num: number = "string"; //报错
let num: number = undefined;

void类型

  • void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void:
function warnUser(): void {
  console.log("This is my warning message");
}
warnUser();
// 注意:声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null
let unusable: void = undefined;

never类型

  • 这个类型和 void 有些相似,也是不返回值,标示函数,比如:
    function error(): never {
      throw new Error("error");
    }
    error();
    
    // function endless(): never {
    //   while (true) {
    //     console.log(111);
    //   }
    // }
    // endless();
    
  • 通俗理解 void 与 never:
  • void 可以理解为一个没有 return 但能执行完毕的函数;
    never 是一个函数体无法结束执行的函数;

any类型

/* 
第三方,无法确定类型,any:宽泛的约束条件
*/
let notSure: any = "4";
notSure = `maybe it is a string`;
notSure = true;

notSure.myName;
notSure.getName();

// any类型 在.调用方法时没有提示
firstName.charCodeAt; //string 有提示
notSure.charCodeAt; //any  没提示

联合类型

/* 
联合类型  可以赋值几个类型
*/
let numberOrString: number | string = 5;
numberOrString = "55";
// numberOrString = true; //报错

数组

/* 
数组 
类型 + [] 表示
*/
// let arrOfNumber: number[] = [1, 2, "str", 4];//报错
let arrOfNumber: number[] = [1, 2, 3, 4];
arrOfNumber.push(5);
// arrOfNumber.push("str");//报错

类数组

/* 
类数组
*/
function test() {
  console.log(arguments); //IArguments
  // arguments.length
  // arguments[1]
  // arguments.forEach//报错,类数组找不到数组原型的方法
  //   let arr: any[] = arguments;//报错
}

元组

/* 
元组   tuple
*/
// 第一项  字符串 第二项 boolean  第三项  数字   用any就丧失了意义
// let user: [string, number] = ["string"];//数组里多参数,少参数也会报错
let user: [string, number] = ["string", 1];
// user = [1, "string"];//报错,类型不能给错,按顺序

枚举

// enums 枚举

// 数字枚举
// enum Direction {
//   Up,
//   Down,
//   Left,
//   Right,
// }
// console.log(Direction.Up); //0
// console.log(Direction[0]); //'Up'

/* 
// tsc 编译后 -> js文件
var Direction;
(function (Direction) {
    Direction[Direction["Up"] = 0] = "Up";
    Direction[Direction["Down"] = 1] = "Down";
    Direction[Direction["Left"] = 2] = "Left";
    Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
console.log(Direction.Up); //0
console.log(Direction[0]); //'Up'
*/


// 字符串枚举
// enum Direction {
//   Up = "UP",
//   Down = "DOWN",
//   Left = "LEFT",
//   Right = "RIGHT",
// }
// const value = "UP";
// if (value === Direction.Up) {
//   console.log("go up!");
// }


// 常量枚举 const enum
const enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}
const value = "UP";
if (value === Direction.Up) {
  console.log("go up!");
}

// tsc 编译后 -> js文件
// var value = "UP";
// if (value === "UP" /* Up */) {
//     console.log("go up!");
// }

interface接口

/* 
interface 接口  :规范 和 契约  规定了样子
对对象的形状进行描述
对类进行抽象
Duck Typing (鸭子类型)

描述  object 的样子
描述 类, 函数 ,数组  都可以用 interface 描述
*/

// 定义一个接口

// 完全匹配形状
interface Person {
  //Person -> IPerson 有的名字前加I 告诉是 Interface
  //这里面属性分隔就是分号分隔,不是逗号
  name: string;
  age: number;
}

// viking -> Person 类型 ,约束 viking 的  object 类型就是Person那样
let viking: Person = {
  //对象里不能少个属性或者多个属性
  name: "songJing",
  age: 20,
};

// 不完全匹配形状 age?: number;
interface PersonLiu {
  name: string;
  age?: number;
}

let vikingLiu: PersonLiu = {
  // age 可以写或者不写
  name: "songJing",
};

// 创建的时候的只读属性  readonly
interface PersonShe {
  readonly id: number; //用于属性上的  放在前面
  name: string;
  age?: number;
}

let vikingShe: PersonShe = {
  id: 1245,
  name: "songJing",
};
// vikingShe.id = 1111;//报错  只读属性,不能重新赋值

函数

// js中函数是一等公民
// 和其他类型平等 传参,存数组,被返回,赋值给变量 等

// function add(x: number, y: number, z: number = 10): number {
//   // y: number, z?: number  必选参数不能位于可选参数后
//   //(x: number, y: number): number   最后的 number   就是返回的类型
//   //  z: number = 10   es6 加默认值&&最后一个参数是可选的
//   if (typeof z === "number") {
//     return x + y + z;
//   } else {
//     return x + y;
//   }
// }

// let result = add(2, 3);

// 函数表达式
const add = function add(x: number, y: number, z: number = 10): number {
  // y: number, z?: number  必选参数不能位于可选参数后
  //(x: number, y: number): number   最后的 number   就是返回的类型
  //  z: number = 10   es6 加默认值  最后一个参数是可选的
  if (typeof z === "number") {
    return x + y + z;
  } else {
    return x + y;
  }
};

// :(x: number, y: number, z?: number) => number   冒号后面是ts中申明类型
// const add2: number = add; //报错
const add2: (x: number, y: number, z?: number) => number = add; //ts类型推测

// let str = "str";
// str = "ddd";
// str = 123;//报错,没有指定类型,ts会自己推测  -> 类型推论

类 :定义一切事物的抽象特点
对象:类的实例
面向对象 oop 封装,继承,多态

类中的修饰符
pulic 共有的
private 属性“name”为私有属性,只能在类“Animal”中访问,外部和子类也不可以访问
protected 子类可以访问属性,外部不可以访问
readonly 只读不能重新赋值

class Animal {
  public name: string;
  //   private name: string;
  //   protected name: string;
  //   readonly name: string;

  //   静态属性
  static categoies: string[] = ["mammal", "bird"];
  //   静态方法:当类中定义和实例的方法没有多大关系时
  static idAnimal(a) {
    return a instanceof Animal;
  }

  constructor(name: string) {
    this.name = name;
  }
  run() {
    return `${this.name} is running`;
  }
}
console.log(Animal.categoies);


const snake = new Animal("lily");
console.log(Animal.idAnimal(snake));

// console.log(snake.run());

// console.log(snake.name);
snake.name = "lucy";
// console.log(snake.name);


class Dog extends Animal {
  bark() {
    return `${this.name} is bark`;
  }
}

const maomao = new Dog("maomao");
// console.log(maomao.run());
// console.log(maomao.bark());


class Cat extends Animal {
  constructor(name) {
    super(name);
    // console.log(this.name);
  }
  run() {
    //super.run  调用父类的方法
    return "Meow," + super.run();
  }
}

const sanhua = new Cat("sanhua");
// console.log(sanhua.run());

类和接口

//两个类都用一个方法,用 interface 定义约束

// class Car {
//   switchRadio() {}
// }

// class Cellphone {
//   switchRadio() {}
// }

// interface  定义约束  implements 抽象类的属性和方法
// 抽象验证类的属性和方法
interface Radio {
  // void 不返回
  switchRadio(triggerL: boolean): void;
}

interface Battery {
  checkBatteryStatus();
}

// 接口也可以继承
interface RadioWithBattery extends Radio {
  checkBatteryStatus();
}

class Car implements RadioWithBattery {
  switchRadio() {}
  checkBatteryStatus() {}
}

// implements Radio, Battery  两个接口,用逗号隔开就行
class Cellphone implements Radio, Battery {
  switchRadio() {}
  checkBatteryStatus() {}
}

泛型

// BUG
// function echo(arg: any): any {
//   return arg;
// }
// const result: string = echo(123);

// 在函数中使用泛型
//泛型出现的动机 ,在函数名称后面加 <>  里面可以写泛型的名称 T  类似占位符或者变量

function echo<T>(arg: T): T {
  return arg;
}
// const str: string = "str";
// const result = echo(str);

// const result: string = echo(true);//报错,不会像之前一样不报错

function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}
const result2 = swap(["str", 12]);
// result2[1].  //调用字符串的方法

// 约束泛型
function echoWithArr<T>(arg: T[]): T[] {
  console.log(arg.length);
  return arg;
}
const arrs = echoWithArr([1, 2, 3]);

interface IWithLength {
  length: number;
}

function echoWithLength<T extends IWithLength>(arg: T): T {
  console.log(arg.length);
  return arg;
}
const str = echoWithLength("str");
const obj = echoWithLength({ length: 10, width: 10 });
const arr2 = echoWithLength([1, 2, 3]);

// echoWithLength(13); //报错,类型“13”的参数不能赋给类型“IWithLength”的参数。



// 在类中使用泛型
class Queue<T> {
  private data = [];
  push(item: T) {
    return this.data.push(item);
  }
  pop(): T {
    return this.data.shift();
  }
}
const queue = new Queue<number>();
queue.push(1);
console.log(queue.pop().toFixed());

const queue2 = new Queue<string>();
queue2.push("str");
console.log(queue2.pop().length);

// 在 interface 中使用泛型
interface KeyPair<T, U> {
  key: T;
  value: U;
}
let kp1: KeyPair<number, string> = { key: 123, value: "str" };
let kp2: KeyPair<string, number> = { key: "22", value: 123 };

let arr: number[] = [1, 2, 3];
let arrTwo: Array<number> = [1, 2, 3];

//
interface IPlus<T> {
  (a: T, b: T): T;
}
function plus(a: number, b: number): number {
  return a + b;
}
function connect(a: string, b: string): string {
  return a + b;
}
const a: IPlus<number> = plus;
const b: IPlus<string> = connect;

命名空间

命名空间:
在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内
同Java的包、.Net的命名空间一样,TypeScript的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。
命名空间和模块的区别:
命名空间:内部模块,主要用于组织代码,避免命名冲突。(不同命名空间,类名称一样也可以)
模块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间。

/* 
命名空间:在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内
*/

import { A, B } from "./animal";

var d = new A.Dog("小黑");
d.eat();

var dog = new B.Dog("毛毛");
dog.eat();

  • animal.ts
    export namespace A {
      interface Animal {
        name: string;
        eat(): void;
      }
      export class Dog implements Animal {
        name: string;
        constructor(theName: string) {
          this.name = theName;
        }
        eat() {
          console.log(`${this.name} 在吃狗粮。`);
        }
      }
      export class Cat implements Animal {
        name: string;
        constructor(theName: string) {
          this.name = theName;
        }
        eat() {
          console.log(`${this.name} 在吃猫粮。`);
        }
      }
    }
    export namespace B {
      interface Animal {
        name: string;
        eat(): void;
      }
      export class Dog implements Animal {
        name: string;
        constructor(theName: string) {
          this.name = theName;
        }
        eat() {
          console.log(`${this.name} 在吃狗粮。`);
        }
      }
      export class Cat implements Animal {
        name: string;
        constructor(theName: string) {
          this.name = theName;
        }
        eat() {
          console.log(`${this.name} 在吃猫粮。`);
        }
      }
    }
    

装饰器

通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上扩展类、属性、方法、参数的功能。
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
装饰器是过去几年js最大的成就之一,以是Es7的标准特性之一

  • 类装饰器:类装饰器在类声明之前被声明(紧靠着类声明)。类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。
  • 装饰器-ES6
  • 装饰器-TypeScript

装饰器的实验支持

  • tsconfig.json
    {
      "compilerOptions": {
        "experimentalDecorators": true //装饰者的实验支持
      }
    }
    

装饰器的写法

  1. 普通装饰器(无法传参)
function logClass(params: any) {
  console.log(params);

  // params 就是当前类
  params.prototype.apiUrl = "动态扩展的属性";

  params.prototype.run = function () {
    console.log("我是一个run方法");
  };
}

@logClass
class HttpClient {
  constructor() {}
  getData() {}
}

var http: any = new HttpClient();
console.log(http.apiUrl);
http.run();
  1. 装饰器工厂(可传参)
function logClass(params: string) {
  return function (target: any) {
    console.log(target);
    console.log(params);

    target.prototype.apiUrl = params;
  };
}

// @logClass("hello")
@logClass("zhainanya")
class HttpClient {
  constructor() {}
  getData() {}
}

var http: any = new HttpClient();
console.log(http.apiUrl);

类装饰器

  • 重载构造函数
  • 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
  • 如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明
function logClass(target: any) {
  console.log(target);
  return class extends target {
    apiUrl: any = "我是修改后的数据";
    getData() {
      this.apiUrl += "----";
      console.log(this.apiUrl);
    }
  };
}
@logClass
class HttpClient {
  public apiUrl: string | undefined;
  constructor() {
    this.apiUrl = "我是构造函数里面的apiUrl";
  }
  getData() {
    console.log(this.apiUrl);
  }
}
var http = new HttpClient();
http.getData();

属性装饰器

  • 属性装饰器表达式会在运行时当作函数被调用,传入下列两个参数:
    • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
    • 成员的名字。
// 类装饰器
function logClass(params: string) {
  return function (target: any) {
    // console.log(target);
    // console.log(params);
  };
}

// 属性装饰器
function logProperty(params: any) {
  return function (target: any, attr: any) {
    console.log(target);
    console.log(attr);
    target[attr] = params;
  };
}

@logClass("xxx")
class HttpClient {
  @logProperty("xxx.com")
  public url: any | undefined;
  constructor() {}
  getData() {
    console.log(this.url);
  }
}

var http: any = new HttpClient();
http.getData();

方法装饰器

它会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。
方法装饰会在运行时传入下列三个参数:

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  • 成员的名字
  • 成员的属性描述符
  • tsc class-decorator.ts --target ES5 -w --experimentalDecorators 运行
  1. 方法装饰器
function get(params: any) {
  return function (target: any, methodName: any, desc: any) {
    console.log(target);
    console.log(methodName);
    console.log(desc);
    target.apiUrl = 123;
    target.run = function () {
      console.log("run");
    };
  };
}

class HttpClient {
  public url: any | undefined;
  constructor() {}

  @get("xxx")
  getData() {
    console.log(this.url);
  }
}

var http: any = new HttpClient();
console.log(http.apiUrl);
http.run();
  1. 方法装饰器
function get(params: any) {
  return function (target: any, methodName: any, desc: any) {
    console.log(target);
    console.log(methodName);
    console.log(desc, desc.value);
    // 修改装饰器的方法,把装饰器方法里面传入的所有参数改为string类型
    var oMthod = desc.value;
    desc.value = function (...args: any[]) {
      args = args.map((value) => {
        return String(value);
      });
      console.log(args, oMthod);
      oMthod.apply(this, args);
    };
  };
}

class HttpClient {
  public url: any | undefined;
  constructor() {}
  @get("maomao")
  getData(...args: any[]) {
    console.log(args, "==");
    console.log("getData=====");
  }
}

var http: any = new HttpClient();
http.getData(123, "xxx");

方法参数装饰器

参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列三个参数

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 方法的名称
  • 参数在函数参数列表中的索引
     function logParams(params: any) {
       return function (target: any, methodName: any, paramsIndex: any) {
         console.log(params);
         console.log(target);
         console.log(methodName);
         console.log(paramsIndex);
         target.apiUrl = params;
       };
     }
     class HttpClient {
       public url: any | undefined;
       constructor() {}
       getData(@logParams("xxx") uuid: any) {
         console.log(uuid);
       }
     }
     var http: any = new HttpClient();
     http.getData(123);
     console.log(http.apiUrl);
    

装饰器执行顺序

  • 属性装饰器 -> 方法装饰器 -> 方法参数装饰器 -> 类装饰器
  • 同一个方法有多个装饰器,会像剥洋葱一样,先从外到内进入,然后由内向外执行。
    function logClass1(params: string) {
      return function (target: any) {
        console.log("类装饰器1");
      };
    }
    function logClass2(params: string) {
      return function (target: any) {
        console.log("类装饰器2");
      };
    }
    function logAttribute(params?: string) {
      return function (target: any, attrName: any) {
        console.log("属性装饰器");
      };
    }
    function logMethod(params?: string) {
      return function (target: any, attrName: any, desc: any) {
        console.log("方法装饰器");
      };
    }
    function logParams1(params?: string) {
      return function (target: any, attrName: any, desc: any) {
        console.log("方法参数装饰器1");
      };
    }
    function logParams2(params?: string) {
      return function (target: any, attrName: any, desc: any) {
        console.log("方法参数装饰器2");
      };
    }
    @logClass1("zhainanya-1")
    @logClass2("zhainanya-2")
    class HttpClient {
      @logAttribute()
      public apiUrl: string | undefined;
      constructor() {}
      @logMethod()
      getData() {
        return true;
      }
      setData(@logParams1() attr1: any, @logParams2() attr2: any) {}
    }
    var http: any = new HttpClient();
    

其他知识点

  • 类型别名,联合类型,类型断言,声明文件 || 第三方模块

类型别名

// type aliases 类型别名
type PlusType = (x: number, y: number) => number;
function sum(x: number, y: number) {
  return x + y;
}
const sum2: PlusType = sum;
————
type FetchKeys = (data:any[]) => Promise<string[]>;
interface FetchKeys {
	(data:any[]): Promise<string[]>
}

联合类型

// 联合类型
type NameResolver = () => string;
type NameOrResolver = string | NameResolver;
function getName(n: NameOrResolver): string {
  if (typeof n === "string") {
    return n;
  } else {
    return n();
  }
}
// type assertion 不确定类型,就访问其中的类型或方法

类型断言

//类型断言 -》 不是类型转换  断言成联合类型里没有的会报错
function getLength(input: string | number): number {
  //   const str = input as String; //interface
  //   if (str.length) {
  //     return str.length;
  //   } else {
  //     const number = input as Number;
  //     return number.toString().length;
  //   }
  if ((<string>input).length) {
    return (<string>input).length;
  } else {
    return input.toString().length;
  }
}

声明文件


// 声明文件 declare   同级文件  jQuery.d.ts

declare var jQuery: (selector: string) => any;

第三方模块

// 第三方模块
// npm install --save @types/jquery
*/

实用工具类型

  • 实用工具类型
    • TypeScript 提供了几种实用程序类型来促进常见的类型转换

常见问题

window 添加自定义属性

  • global.d.ts文件
declare global {
  interface Window {
    RM_FONT_FACE: string;
    RM_ANIMATION: string;
  }
}

export {};

interface 和 type 区别

TypeScript 中的 interface 和 type 都可以用来定义对象的形状或者函数签名,但它们在某些方面有所不同。下面是 interface 和 type 的一些主要区别:

  1. 扩展性(Extensibility)
    interface:可以被扩展以构建新的接口。通过使用 extends 关键字,接口可以扩展一个或多个现有的接口。
    type:不可以被扩展,但可以通过交叉类型(使用 & 操作符)来组合现有的类型。
  2. 重复声明(Duplicate Declarations)
    interface:可以在同一作用域内多次声明同一个接口,其成员会自动合并。
    type:不允许同名的重复声明。
  3. 使用场景(Use Cases)
    interface:推荐在定义对象的形状或实现类的场景中使用。它支持声明合并,这在声明文件(例如 .d.ts 文件)或声明类和函数库时十分有用。
    type:适用于定义联合类型、交叉类型、元组类型等复杂类型。类型别名可以声明几乎任何类型,包括基本类型、联合类型和交叉类型。
  4. 实现(Implementation)
    interface:可以通过类来实现(使用 implements 关键字)。
    type:不能被类实现,因为它们可以代表非对象类型(如原始类型、联合和交叉类型)。
  5. 性能(Performance)
    interface:在某些情况下,interface 可能会得到更好的性能,因为它们更容易被TypeScript编译器优化。
    type:由于类型别名可能会直接展开,所以在某些复杂的类型组合中可能会导致编译器和IDE性能下降。
  • 7
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,下面是TypeScript小白入门教程: 1. 安装TypeScript 首先,你需要安装TypeScript。你可以通过以下命令在全局安装TypeScript: ``` npm install -g typescript ``` 2. 创建一个TypeScript文件 创建一个新的文件,将其命名为`app.ts`。 在文件中,输入以下代码: ```typescript function greet(name: string) { console.log("Hello, " + name.toUpperCase() + "!!"); } greet("World"); ``` 3. 编译TypeScript文件 在命令行中进入到你的项目目录,并运行以下命令: ``` tsc app.ts ``` 这将编译你的TypeScript文件并生成一个JavaScript文件。 在此示例中,生成的JavaScript文件名为`app.js`。 4. 运行JavaScript文件 现在,你可以运行JavaScript文件。 在命令行中运行以下命令: ``` node app.js ``` 这将输出“Hello,WORLD!” 5. 定义变量类型 TypeScript需要在变量声明时指定其类型。 在下面的示例中,我们定义了一个名为`age`的变量,并将其类型设置为`number`: ```typescript let age: number = 25; ``` 6. 函数参数类型 与变量类似,TypeScript还需要在函数参数声明中指定类型。 在下面的示例中,我们定义了一个名为`greet`的函数,并将其参数的类型设置为`string`: ```typescript function greet(name: string) { console.log("Hello, " + name.toUpperCase() + "!!"); } greet("World"); ``` 7. 类型注释 TypeScript允许你使用类型注释来指定变量或函数的类型。 在下面的示例中,我们使用类型注释来指定变量`age`的类型: ```typescript let age: number; // age是一个数字类型的变量 ``` 8. 接口 接口是一种定义对象结构的方式。 在下面的示例中,我们定义了一个名为`Person`的接口,该接口具有两个属性:`name`和`age`: ```typescript interface Person { name: string; age: number; } let person: Person = { name: "John", age: 25 }; ``` 9. 类 类是一种定义对象的方式,可以具有属性和方法。 在下面的示例中,我们定义了一个名为`Person`的类,该类具有一个名为`name`的属性和一个名为`greet`的方法: ```typescript class Person { name: string; constructor(name: string) { this.name = name; } greet() { console.log("Hello, " + this.name + "!!"); } } let person = new Person("John"); person.greet(); ``` 这就是一个简单的TypeScript入门教程。希望对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值