typescript

一、安装和使用

  • 下载 node
  • 下载typescript(npm i -g typescript
  • tsc -v 查看 typescript版本
  • tsx XXX.ts 编译 ts 文件,生成对应的 js 文件

注意:如果生成了 js 文件,ts 文件有变量重复声明的报错,可以执行tsc -init命令,会生成一个 tsconfig.js 文件,如果有 error TS2403: Subsequent variable declarations must have the same type. Variable 'AbortSignal' must be of type '{ new (): AbortSignal; prototype: AbortSignal; abort(reason?: any): AbortSignal; timeout(milliseconds: number): AbortSignal; }', but here has type '{ new (): AbortSignal; prototype: AbortSignal; }'.,或者declare var AbortSignal,或者Type 'Server' is not generic.报错,可以执行 npm install @types/node -D

二、类型声明

  • 类型声明
    · 类型声明时 TS 非常重要的一个特点
    · 通过类型声明可以指定 TS 中变量(参数、形参)的类型
    · 指定类型后,当为变量赋值时,TS 编译器会自动检查值是否符合类型声明,符合则赋值,否则报错
    · 简而言之,类型声明给变了设置了类型,使得变量只能存储某种类型的值
  • 自动类型判断
    · TS 拥有自动的类型判断机制
    · 当对变量的声明和赋值是同时进行的,TS 编译器会自动判断变量的类型
    · 所以如果变量的声明和赋值是同时进行的,可以省略掉类型声明

1、变量

  • 声明一个变量a,同时指定它的类型为 number
  • 后续赋值或使用时,类型必须跟声明时的类型一致,否则会报错
  • 如果变了的声明和赋值时同时进行的,可以省略类型,ts 会自动对变量进行类型检测
  • 后续使用或赋值,同样要跟之前的类型一致,否则会报错
//声明一个变量a,同时指定它的类型为 number
//后续赋值或使用时,类型必须跟声明时的类型一致,否则会报错
let a : number;
a = 1;

//如果变了的声明和赋值时同时进行的,可以省略类型,ts 会自动对变量进行类型检测
//后续使用或赋值,同样要跟之前的类型一致,否则会报错
let b = true;
 b = false;

2、函数

  • 声明函数时,可以指定参数的类型
  • 调用函数时,入参类型要跟声明时的类型一致,另外,入参个数也要和函数声明时接收的参数个数一致
//声明函数时,可以指定参数的类型
//调用函数时,入参类型要跟声明时的类型一致,另外,入参个数也要和函数声明时接收的参数个数一致
//函数声明时,括号后面也可以规定类型,该类型是函数返回值的类型

function sum (a: number, b: number): number{
  return a + b
}

sum(123,456)
// sum(123,"456")·//报错
// sum(123,456,789)·//报错

三、类型

在这里插入图片描述

1、any和unknown的区别

let a : number;
a = 1;
let b = true;
b = false;

let e : any;
e = "12"
a = e

let f : unknown;
f = "1"
b = unknown //报错
  • 任何类型的变量都可以被赋值为 any 类型,比如上面代码中的 a = e(不仅会把 e 的类型检测关掉,还会把 a 的类型检测也关掉),但是 unkown 不行,unkown类型的数据 不能赋值给其他类型的变量
  • unknown 实际上是一个类型安全的 any

2、定义对象类型

//{}用来指定对象中可以包含哪些属性
//语法:{属性名:属性值,属性名:属性值}
//在属性名后加上?,表示属性是可选的,但是需要在定义时罗列出来
//使用[propname:string]:any,是上面?的简写,不需要一一罗列
//定义对象

let obj : {name: string, age? :number};
obj = {name: "张三",age: 18}
obj = {name: "张三"}

// let obj : {name: string,[propname:string]:any};
// obj = {name: "张三",age: 18, sex: "男"}

3、定义数组

//设置数组的类型声明
//数组的类型声明:
//  类型[]
//  Array<类型>

// let arr: string[];   //表示字符串数组
// arr = ["1","2"]
// let arr: number[];   //表示数字类型数组
// arr = [1,2]
let arr: Array<string>;   //表示字符串数组
arr = ["1","2"]

4、定义函数

//设置函数结构的类型声明:
//语法:(形参:类型,形参:类型......) => 返回值
// let d: () => any
let d: (a: number, b: number) => number
d = function (a, b) {
  return a + b;
}
类型断言

可以用来告诉解析器的实际类型。
语法:

  • 变量 as 类型(a as string)
  • <类型>变量(a)
let a : number;
a = 1;
let f : unknown;
f = 1
a = f as number

四、编译选项

1、自动编译文件

编译文件时,使用 -w 指令后,TS 编译器会自动监视文件的变化,并在文件变化时对文件进行重新编译,生成对应的 js 文件。
示例:

$ tsc demo.ts -w	//监听某个ts文件并进行编译
$ tsc -w 	//检测整个目录中的ts文件并进行编译

2、自动编译整个项目

  • 如果直接使用 tsc 指令,则可以自动将当前项目下的所有 ts 文件编译为 js 文件
  • 但是能直接使用 tsc 命令的前提是,要先在项目目录下创建 ts 的配置文件 tsconfig.json(可手动新建文件,也可使用tsc -init命令创建)
  • tsconfig.json 是一个 JSON 文件,添加配置文件后,只需要 tsc 命令即可完成对整个项目的编译
1.配置选项
1)include
  • 定义希望被编译文件所在的目录
  • 默认值:["**/*"](** 表示任意目录,* 表示任意文件)
  • 示例:
{
  //src目录和test目录下的文件都会被编译
  "include": [
    "src/**/*",
    "test/**/*"
  ]
}
2)exclude
  • 和include相反,定义需要排除在外的目录
  • 默认值:["node-modules","bower_componts","jspm_packages"]
  • 示例:
{
  //目录src下的所有文件都需要编译
  "include": [
    "src/**/*",
    // "test/**/*"
  ],
  //目录test下的所有文件都不需要编译
  "exclude": [
    "test/**/*"
  ]
}
3)extends
  • 定义被继承的配置文件(相当于引入一个配置文件)
  • 示例:
//当前配置的文件中会自动包含 config 目录下的 base.json 中的所有配置信息
"extends": "./configs/base"
4)files
  • 指定被编译文件的列表,只有需要编译的文件少时才会用到(与 include 类似,只是 include 要配置目录,files 要配置文件)
  • 示例:
"files": [
	"core.ts",
	"sys.ts",
	...
]
5)compilerOptions
  • 编译选项是配置文件中非常重要也比较复杂的配置选项。
  • 在 compilerOptions 中包含多个子选项,用来完成对编译的配置
"compilerOptions": {
  // target 用来指定 ts 被编译为的 ES 版本
  //'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021'...
  "target": "ES5",
  // module 指定要使用的模块化的规范
  //“none","commonjs","amd","es6"...
  "module": "commonjs",
  // lib 指定代码运行时所包含的库(宿主环境)
  "lib": [
    "ES5",
    "DOM"
  ],
  // outDir 用来指定编译后文件所在的目录
  "outDir": "./dist",
  // outFile 将所有全局作用域中的代码合并到同一个文件中
  // "outFile": "./dist/app.js"
  // allowJs 师傅对 js 文件进行编译,默认是false
  "allowJs": true,
  // checkJs 是否以 ts 的标准去检查 js 代码是否符合语法规范,默认是 false
  "checkJs": true,
  // removeComments 生成的 js 文件是否需要移除注释
  "removeComments": true,
  // noEmit 执行编译过程,但是不生产编译后的文件,可用作代码检查
  "noEmit": true,

  // strict 下面所有严格检查的总开关
  "strict": true,
  // noEmitOnError 当有错误时,不生成编译后的文件
  "noEmitOnError": false,
  // alwaysStrict 用来设置编译后的文件是否使用严格模式
  "alwaysStrict": true,
  // noImplicitAny 不允许隐式的any类型
  "noImplicitAny": true,
  // noImplicitThis 不允许不明确类型的 this
  "noImplicitThis": true,
  // strictNullChecks 严格检查空值
  "strictNullChecks": true
}

五、使用webpac打包ts代码

1.初始化项目

进入根目录,执行命令npm init -y,命令执行完会自动创建一个 package.json 文件

2.下载构建工具

npm i -D webpack webpack-cli typescript ts-loader

3.根目录下创建 webpack.config.js 文件

配置打包的入口、出口和tsloader

const path = require("path")

module.exports = {
  entry: "./src/main.ts",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
    clean: true
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: "ts-loader",
        exclude: /node_modules/
      }
    ]
  },
  mode: "production"
}

4.根目录创建 tsconfig.json 文件,配置基本信息

{
  "compilerOptions": {
    "target": "ES2015",
    "module": "ES2015",
    "strict": true
  }
}

5.修改 package.json 文件,配置打包命令

  "scripts": {
    "build": "webpack"
  },

6.根目录下的 src 文件中,创建 main.ts 文件

function sum (a: number, b: number): number {
  return a + b
}
console.log(sum(123,456));

7.执行打包命令

npm run build
命令执行完,会在根目录下生成 dist/bundle.js 文件

六、面向对象

1、类

  • 直接定义的属性是实例属性,需要通过对象的实例去访问:
    let per = new Person();
    per.name

  • 使用 static 开头的属性(方法)是静态属性(方法),或者类属性(方法),可以直接通过类去访问:
    Person.job;
    Person.sleep

  • readonly 开头的属性表示是只读属性,无法被修改

/*
直接定义的属性是实例属性,需要通过对象的实例去访问:
  let per = new Person();
  per.name

使用 static 开头的属性(方法)是静态属性(方法),或者类属性(方法),可以直接通过类去访问:
  Person.job;
  Person.sleep

readonly 开头的属性表示是只读属性,无法被修改
*/

class Person {
  name: string = "张三";
  age: number = 18
  readonly sex = "男"

  static job: string = "程序员";
  eat() {
    return "吃饭";
  }
  static sleep() {
    return "睡觉";
  }
}
let per = new Person()
console.log(per.name);
console.log(per.age);
console.log(Person.job);
console.log(per.eat());
console.log(Person.sleep());

2、构造函数和 this

class Dog{
  name: string
  age: number
  //constructor 被称为构造函数
  //构造函数会在对象创建时调用
  constructor(name: string, age: number) {
    //示例方法中,this表示当前的示例
    //在构造函数中,当前对象就是当前创建的那个对象
    //可以通过 this 向新建的对象中添加属性
    this.name = name
    this.age = age
  }
  eat() {
    return "吃饭"
  }
}
let dog1 = new Dog("小黑",5)
let dog2 = new Dog("小白", 6)
console.log(dog1);
console.log(dog2);

3、继承

/**
 * Dog extends Animal
 * 此时,Animal 被称为 父类,Dog 被称为 子类
 * 使用继承后,子类会拥有父类所有的方法和属性
 * 使用继承,可以将多个类中共有的代码写在一个父类中
 *    这样值需要写一次就可以让所有子类都同时拥有父类的属性
 *    如果希望在子类中添加一些父类中没有的属性或者方法,可以在子类中直接加
 * 如果在子类中添加了和父类相同的方法,则子类方法会覆盖父类的方法
 *    这种子类覆盖掉父类的形式,称为 方法重写
 */

class Dog extends Animal {
  talk(){
    console.log("汪汪汪");
  }
  run() {
    console.log(`${this.name}在跑`);
  }
}
class Cat extends Animal {
  talk(){
    console.log("喵喵喵");
  }
}

let dog = new Dog("小狗", 6)
let cat = new Cat("小猫", 8)

console.log(dog);
dog.talk()
dog.run()
console.log(cat);
cat.talk()

在这里插入图片描述

4、super 关键字

如果子类需要额外的属性,那么就要用到super(子类的构造函数中,调用父类构造函数,处理父类所需的属性)
否则,不需要用super

class Animal {
  name: string
  constructor(name: string) {
    this.name = name
  }
  talk() {
    console.log("动物在叫");
  }
}

class Dog extends Animal {
  age: number
  constructor(name: string, age: number) {
    //如果在子类中写了构造函数,在子类构造函数中必须对父类的构造函数进行调用
    //如果不重写,默认会自动调用父类的构造函数
    super(name)   //调用父类的构造函数,传入父类构造函数所需要的参数
    this.age = age
  }
  talk() {
    //在类的方法中,super 就表示当前类的父类
    // super.talk()  //动物在叫
    console.log("汪汪汪");
  }
}

let dog = new Dog("小狗", 2)
console.log(dog);
dog.talk()

5、抽象类

  • 以abstract开头的类是抽象类
    抽象类和其他类区别不大,只是不能用来创建对象
    抽象类就是专门用类被继承的类()父类
  • 抽象类中还有抽象方法,也是需要以 abstract 开头,在抽象类中只做声明,没有方法体,需要在子类中定义方法
/**
 * 以abstract开头的类是抽象类
 *    抽象类和其他类区别不大,只是不能用来创建对象
 *    抽象类就是专门用类被继承的类(父类)
 */
abstract class Animal {
  name: string
  constructor(name: string) {
    this.name = name
  }
  //定义一个抽象方法
  //抽象方法使用 abstract 开头,没有方法体
  //抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
  abstract sayHello():void;
}
class Cat extends Animal {
  sayHello() {
    console.log("喵喵喵");
  }
}
let cat = new Cat("小猫")

6、接口

/**
 * 接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
 * 同时接口也可以当成类型声明去使用
 * 它是一种规范
 */

interface myInter {
  name: string,
  age: number
}
//如果定义同名的接口,接口数据会合并
interface myInter {
  gender: string
}

let obj: myInter = {
  name: "张三",
  age: 18,
  gender: "男"
}

/**
 * 接口可以在定义类的时候去限制类的结构
 *    接口中所有的属性都不能有实际的值
 *    接口只定义对象的结构,而不考虑实际值
 *        接口中所有的方法都是抽象方法
 */
interface myInterface {
  name: string,
  sayHello():void
}
/**
 * 定义类是,可以使类去实现一个接口
 *    实现接口就是使用类满足接口的要求
 */
class MyClass implements myInterface {
  name: string
  constructor(name: string) {
    this.name = name
  }
  sayHello() {
    console.log("你好啊");
  }
}
let myclass = new MyClass("张三")
console.log(myclass);

7、属性的封装

TS 可以在属性前添加属性的修饰符

  • public 修饰的属性可以在任意位置(父类、子类、实例)访问(修改),是默认值
  • private 私有属性,私有属性只能在类的内部访问(修改)
    • 通过在类中添加方法,是得私有属性可以被外部访问
  • protected 受包含的属性,只能在当前类和当前类的子类中访问(修改)
/**
 * TS 可以在属性前添加属性的修饰符
 * public 修饰的属性可以在任意位置(父类、子类、实例)访问(修改),是默认值
 * private 私有属性,私有属性只能在类的内部访问(修改)
 *    - 通过在类中添加方法,是得私有属性可以被外部访问
 * protected 受包含的属性,只能在当前类和当前类的子类中访问(修改)
 */
class Person {
  name: string;
  private _age: number;
  private gender: string;
  constructor(name: string, _age: number, gender: string) {
    this.name = name;
    this._age = _age;
    this.gender = gender;
  }
  getGender() {
    return this.gender
  }
  /**
   * getter 方法分用来读取属性
   * setter 方法用来设置属性
   *    它们被称为属性的存取器
   */
  get age() {
    return this._age
  }
  set age(value: number) {
    //使用 set 可以统一处理设置的值,如 name 属性则可以被任意修改,
    //属性可以任意被修改会将导致对象中的数据变得非常不安全
    if(value > 0) {
      this._age = value
    }
  }
}
let per = new Person("张三", 20, "男")
per.age = 18
console.log(per.age);
console.log(per.getGender());
console.log(per);

// protected 
class B {
  protected num: number
  constructor(num: number) {
    this.num = num
  }
}
class C extends B{
  test() {
    console.log("----"+this.num);
    
  }
}
let c = new C(18)
c.test()
console.log(c.num);   //受 protected 影响,实例无法访问 num

8、泛型

/**
 * 在定义函数或类时,如果遇到类型不明确就可以使用泛型
 */
function fn<T>(a: T): T {
  return a
}
//可以直接调用具有泛型的函数
fn(1);  //不指定泛型,TS 可以自动对类型进行推断
fn<number>(1);  //指定泛型

//泛型可以同时指定多个
function fn2<T, K>(a: T, b: K): T {
  console.log(b);
  return a
}
fn2("hello", 12);
fn2<string, number>("hello", 12);


interface inter {
  length: number
}
//T extends inter 表示泛型 T 必须是 inter 实现类(子类)
function fn3<T extends inter>(a: T): number {
  return a.length
}
class MyClass<T> {
  name: T;
  constructor(name: T) {
    this.name = name
  }
}
let mc = new MyClass<string>("张三")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值