TypeScript笔记

辰漪
2023-05-25 / 0 评论 / 49 阅读 / 正在检测是否收录...

TS简介

  • ts基于javascript语言构建
  • ts是js的超集,对js语法进行了扩展
  • 可以在任何支持js的平台运行
  • ts支持es的新特性,并且还新增了一些新的特性,例如一些api,工具,抽象类啊什么的
  • ts支持js的类型,并且新增了一些其他类型(枚举类...)
  • 有着丰富的配置选项
  • ts通过配置可以编译成任意版本的js es6 es3等等

TS开发环境

  • 下载并安装nodejs
  • 使用npm全局安装typescript编译器

    • 打开cmd或者powershell(管理员打开)
    • 输入 npm i -g typescript
  • 测试typescript

    • 新建一个文件 hello.ts
    • 打开cmd输入以下命令进行编译ts文件
    • tsc hello.ts
    • 查看是否编译成功,会多一个hello.js文件

TS类型声明

  • 类型
类型描述类型描述
number数字string字符串
boolean布尔值(true, false)字面量限制变量的值就是该字面量的值
any任意类型unknown类型安全的any
vold空值,没有值(undefined)never没有值,不能是任何值
objectjs对象arrayjs数组
tuple元组,ts新增类型,固定长度数组enum枚举,ts新增类型
  • 先变量类型声明,再进行赋值
let a: number;
a = 10;
a = 20;
// a = 'string' // 代码会报错,不能将类型“string”分配给类型“number” 
console.log(a);
  • 类型声明和赋值同时进行(ts会自动判断变量类型)
let c = 10; // 相当于 let c: number = 10
c = 20;
c = 'string'; // 代码报错 不能将类型“string”分配给类型“number”
  • 函数参数,函数返回值类型声明
function sum(a: number, b: number): number{
  return a + b;
}
// const count = sum(10, 20, 30); // 代码报错:应有 2 个参数,但获得 3 个
const count = sum(10, 20);
console.log('count: ' + count);
  • 字面量类型声明 --- 使用 “|” 连接多个类型
let e: 10 | 13;
e = 10;
e = 13;
// e = 12; // 不能将类型“12”分配给类型“10”
  • 多类型声明 --- 使用 “|” 连接多个类型
let d: number | string;
d = 1;
d = 'string';
// d = false; // 代码报错:不能将类型“boolean”分配给类型“string | number”
  • any和unknown类型
let f: any; // 显式any
// let f; // 隐式any ts会自动检测类型
f = 1;
f = false;
f = 'string';

let g: boolean;
g = f // any 类型的值 赋值给 其他类型的变量 不会发生报错

let h: unknown;
h = 2;
h = 'string';
h = true;
// g = h; // 代码报错:不能将类型“unknown”分配给类型“boolean”

// 我们自己知道 h 就是一个 boolean类型 ts检测器不知道 我们可以自己判断
if (typeof h === 'boolean') {
  g = h; // 此时不会报错
}
// 或者使用 ts 类型断言 直接告诉解析器 变量的类型
g = h as boolean;
g = <boolean> h;
  • void和never类型
// void 表示空值,该函数没有返回值或者返回值是undefined
function fn(): void{
  // return 123 代码报错:不能将类型“number”分配给类型“void”
  return undefined
}
// never 表示没有值,永远不会有值,没有终点
function fn1(): never{
  throw new Error('报错了!') // 抛出错误 程序终止
}
  • object和array类型
// object 类型
let a: object;
a = {};
a = function () {};
// 使用对象字面量声明类型 --- 可以指定对象中的属性以及类型
// 属性名后面加?表示该属性是可选的
let b: {name: string, age?: number};
// b = {}; //代码报错:类型“{}”缺少类型“{ name: string; age: 18; }”中的以下属性
b = {name: '张三', age: 18};
// b = {name: '张三', age: 18, a: 1, b: 2} 代码报错:只能指定类型中已经存在的属性

// 如果还想要其他属性,可以使用[propName: string]: any;
let c: {name: string, age?: number, [propName: string]: any};
c = {name: '李四', a: 1, b: 2};


// 使用箭头函数的形式,设置函数的结构类型
let f: (a: number, b: number) => number;
f = function (a, b) {
  return a + b;
}
// f = function(a: string, b) { // 两个a的类型不符合
//   return 'ss'; // 代码报错:返回值必须符合上述定义返回值类型
// }

// array 类型 --- 两种声明写法
// 1. 类型[] 2. Array<类型>
let g: string[]; // 字符串数组
g = ['f', 'g'];
// g = ['f', 'g', 1] // 代码报错:不能将类型“number”分配给类型“string”
let h: Array<number>; // 数字数组
  • 元组类型 --- 固定长度的数组
// 元组类型 --- 固定长度的数组
// 语法:[string, string, number, ...]

let r: [string, number, boolean];
r = ['ss', 12, true]; // 长度符合,且,每个元素的类型也要符合
  • 枚举类型 --- 枚举类
// enum枚举类型
// 和不同的js对象本质上没什么区别
// 对于开发者来说,相比于拿值类比较判断,枚举类型更加易懂
// 提高易读性和可维护性
// 语法:enum 枚举名称 {}
enum Gender {
  male, // 默认male = 0
  female // 默认female = 1
}
// let y: {name: string, gender: 0|1};
// console.log(y.gender === 1);
let y: {name: string, gender: Gender};
y = {name: '张三', gender: Gender.male}
console.log(y.gender === Gender.male);
  • |表示或者 和 &表示且
// |表示或者 和 &表示且
let u: 1 | 2;
// 对象,既要满足第一个也要满足第二个,即name和age两个属性都要有
let i: {name: string} & {age: number};
  • 类型别名 --- 如果一个类型特别长,可以写别名代替
type numType = 1 | 2 | 3 | 4
let o: numType;
let p: numType;
o = 1;
p = 2;

TS编译选项配置

  • 自动编译ts文件(只能自动编译单个ts文件)
tsc index.ts -w // 通过-w指令,监听文件的变化,自动编译
  • 通过ts配置文件(tsconfig.json)进行编译
  • 项目目录下新建tsconfig.json文件
  • 此时可直接使用tsc -w命令,自动编译并监听,项目所有ts文件
  • tsconfig.json常用配置项

    • include --- 包含要编译的文件目录以及文件

      • ** 表示任意目录,*表示任意文件
      • 写法:"include": ["./src/**/*"] src文件夹下的ts文件都要进行编译
    • exclude --- 排除不需要编译的文件目录和文件

      • 默认值:["node_modules", "bower_components", "jspm_packages"]
      • ** 表示任意目录,*表示任意文件
      • 写法:"exclude": ["./exclude/**/*"] exclude文件夹下的ts文件不需要编译
    • extends --- 继承外部json配置文件

      • 写法:"extends": "./config/xxx.json"
    • files --- 小项目,或者编译的文件很少,才会使用到这个选项,单独配置要编译的文件

      • 写法:"files: ["./src/index.ts", "./src/home.ts"]"
    • compilerOnSave --- 保存ts文件时,是否进行即时编译,布尔值 默认flase
    • buildOnSave --- 保存ts文件时,是否对文件进行编译,以及其他文件的构建任务,布尔值 默认false
    • compilerOptions --- 编译器选项,如何编译ts文件,编译成什么版本都在这个选项中进行配置

      • target --- 指定要编译成那个版本的js

        • 默认值:ES3
        • 可接受:"ES3", "ES5", "ES6", "ES2015", "ES2016", "ES2017", "ES2018","ES2019", "ES2020", "ES2021", "ES2022", "ES2023", "ESNext"
      • module --- 指定编译成的js文件使用什么模块化规则

        • 默认值:target为es3 |es5,默认则是CommonJS规则,否则默认是 ES6/ES2015规则
        • 可接受:"CommonJS", "AMD", "System", "UMD", "ES6", "ES2015","ES2020", "ESNext", "None", "ES2022", "Node16", "NodeNext"
      • lib --- 指定运行环境所需要使用的库

        • 默认值:浏览器的一些环境,如:DOM,ES5
        • 可接受: "ES5", "ES6", "ES2015", "ES2015.Collection", "ES2015.Core", "ES2015.Generator", "ES2015.Iterable", "ES2015.Promise", "ES2015.Proxy", "ES2015.Reflect", "ES2015.Symbol.WellKnown", 等还有许多
        • 写法:"lib: ["ES6", "DOM"]"
      • outDir --- 指定ts文件编译后所在的目录
      • outFile --- 将编译后的代码合并到同一个文件(此时模块化规范得是amd|system),不常用
      • allowJs --- 是否对js文件也进行编译

        • 默认值:false,不对js文件进行编译
      • checkJs --- 是否对js文件语法进行检查

        • 默认值:false,不对js文件语法进行检查
      • removeComments --- 是否移除注释

        • 默认值:false, 不移除注释
      • noEmit --- 不要生成编译文件

        • 默认值:false,允许生成编译文件
      • noEmitOnError --- 不生成编译文件,如果文件存在错误的话

        • 默认值:false,有错误,照样生成编译后的文件
      • strict --- 严格检查的总开关

        • 默认值:false,关闭
      • alwaysStrict --- 启用js严格模式

        • 默认值:false, 不开启js严格模式
      • noImplicitAny --- 不允许隐式的any类型

        • 默认值:false,允许隐式的any类型
      • noImplicitThis --- 不允许不明确的this

        • 默认值:false, 允许不明确的this
      • strictNullChecks --- 严格检查空值

        • 默认值:false,不检查空值
      • strictBindCallApply --- 严格检查call,apply,bind的参数列表
      • strictFunctionTypes --- 严格检查function函数的类型
      • strictPropertyInitialization --- 严格检查属性是否初始化
      • noFallthroughCasesInSwitch --- 检查switch语句包含正确的break
      • noImplicitReturns --- 检查函数没有隐式的返回值
      • noUnusedLocals --- 检查未使用的全局变量
      • noUnusedParameters --- 检查未使用的参数
      • allowUnreachableCode --- 允许不可达代码

在webpack中使用ts

  • 初始化项目

    • 创建项目文件夹
    • npm init -y 生成包管理文件package.json文件
  • 下载项目依赖包
    # webpack项目
    npm i webpack webpack-cli webpack-dev-server html-webpack-plugin -D
    # ts文件编译成js文件
    npm i ts-loader typescript -D
    # 使用bable对js兼容性做处理
    npm i babel-loader @babel/core @babel/preset-env core-js -D
  • 创建webpack.config.js配置文件
    const path = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    module.exports = {
      // 入口文件
      entry: path.resolve(__dirname, 'src/main.ts'),
      output: {
        path: path.resolve(__dirname, 'dist'), // 输出文件目录
        filename: 'bundle.js', // 输出文件名称
        environment: {
          arrowFunction: false, // 关闭webpack打包后的自调用箭头函数
        },
        clean: true, // 每次打包清空上次目录
      },
      // webpack-dev-server
      devServer: {
        host: '127.0.0.1',
        port: 9223, // 端口号
        open: true
      },
      module: {
        // 处理ts文件
        rules: [
          {
            test: /\.ts$/,
            // use: "ts-loader",
            use: [
              {
                loader: "babel-loader",
                options: {
                  presets: [
                    ["@babel/preset-env", {
                      "targets": {"ie": "11", "chrome": "58"},
                      corejs: "3",
                      useBuiltIns: "usage" // 按需加载js垫片 使浏览器支持最新api
                    }]
                  ]
                }
              },
              "ts-loader"
            ],
            exclude: path.resolve(__dirname, 'node_modules')
          }
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: path.resolve(__dirname, 'public/index.html')
        })
      ],
      optimization: {
        minimize: true,
      },
      resolve: {
        extensions: [".js", ".ts"] // 快捷导入 无需写后缀
      },
      mode: 'production' // development production
    }
    
  • 创建tsconfig.json配置文件
    {
      "compilerOptions": {
        "target": "ES2015",
        "module": "ES2015",
        "strict": true
      }
    }

class 类

类的定义

class 类名 {
    // 实例属性 通过实例.属性名调用
    属性名: 类型 = 值
    属性名: 类型 = 值
    // 静态属性 通过类.属性名调用
    static 属性名: 类型 = 值
    // 只读属性 通过实例.属性名调用 不可修改
    readonly 属性名: 类型 = 值
    // 只读静态属性 通过类.属性名调用 不可修改
    static readonly 属性名: 类型 = 值
    // 构造函数
    constructor(参数: 类型) {
        this.属性名 = 参数
    },
    方法名() {
        ...
    }
}

类的使用

class Dog {
  // 定义属性
  name: string
  age: number
  constructor(name: string, age: number) {
    // 构造函数中进行赋值
    this.name = name
    this.age = age
  }
  say() {
    console.log(this.name + ': 汪汪汪');
  }
}
const dog1 = new Dog('小黑', 2)
const dog2 = new Dog('小白', 1.5)
console.log(dog1.name , dog1.age);
dog1.say()
dog2.say()

类的继承

  • 使用extends关键字实现继承
  • 如果要给子类添加新的属性,则必须要在子类构造函数中,使用关键字super类,调用一下父类构造函数,并传递参数,此时才可以在子类添加新的属性
class Animal {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  say() {
    console.log(this.name + '在叫');
  }
}


/* 
  使用 extends 实现类继承
  如果在子类中写了constructor构造函数,必须要调用super()重写调用一下父类构造函数,否则父类构造函数不会生效
*/
class Dog extends Animal {
  state: number
  constructor(name: string, age: number, state: number) {
    super(name, age)
    // 给子类创建新的属性
    this.state = state
  }
  // 方法重写
  say() {
    console.log(this.name + ': 汪汪汪!');
  }
}
class Cat extends Animal{
  // 方法重写
  say() {
    console.log(this.name + ': 喵喵喵!');
  }
}
const dog = new Dog('小黑', 2, 0)
const cat = new Cat('小白', 1.5)
dog.say()
console.log(dog.state);
cat.say()

抽象类

  • 使用 abstract class类定义的类,就是抽象类
  • 抽象类不能用来创建对象,只能被继承
  • 抽象类可以创建抽象方法,抽象方法不能有实现
  • 子类必须要对抽象方法进行重写
(function(){
  /**
   * 抽象类 使用 abstract class类定义
   * 抽象类不能用于创建对象,只能用来继承
   * 抽象类可以创建抽象方法,抽象方法不能有实现,子类必须要对抽象方法进行重写
   */
  abstract class Animal {
    name: string
    age: number
    constructor(name: string, age: number) {
      this.name = name
      this.age = age
    }
    abstract say(): void
  }


  class Dog extends Animal {
    // 方法重写
    say() {
      console.log(this.name + ': 汪汪汪!');
    }
  }
  class Cat extends Animal{
    // 方法重写
    say() {
      console.log(this.name + ': 喵喵喵!');
    }
  }
  const dog = new Dog('小黑', 2, 0)
  const cat = new Cat('小白', 1.5)
  dog.say()
  cat.say()
})()

类的属性封装

  • 类的三种属性修饰符

    • public(默认值):属性可以在类,子类,实例对象中进行修改
    • protected:属性,只能在类,子类中进行修改
    • private: 私有属性,只能在类中进行修改
  • 私有属性访问,就需要属性封装,类中提供修改和获取属性的方法,降低容错率,避免随意修改属性,使属性更加的安全
    (function() {
      class Person {
        name: string; // 类,子类,实例对象中都可以进行修改
        protected age: number; // 只能在类,子类中进行修改
        private sex: number; // 只能在类中进行修改,私有属性
        constructor(name: string, age: number, sex: number){
          this.name = name;
          this.age = age;
          this.sex = sex;
        }
        getSex(){
          return this.sex;
        }
        setSex(sex: number) {
          if (sex !== 0 && sex !== 1) return // 可以对数据做一些限制,不能随意修改
          this.sex = sex;
        }
      }
      const p1 = new Person('张三', 18, 0);
      // console.log(p1.age); // 代码报错:只能在类“Person”及其子类中访问
      // p1.age = 20; // 代码报错:只能在类“Person”及其子类中访问
      p1.name = '李四';
      // p1.sex = 10; 属性“sex”为私有属性,只能在类“Person”中访问
      console.log(p1.getSex());
      p1.setSex(1);
      console.log('修改后的p1:', p1);
    })()

类的简化写法

  • 直接在构造其中声明属性,需要添加修饰符
// class A {
//   name: string;
//   age: number;
//   constructor(name: string, age: number){
//     this.name = name;
//     this.age = age;
//   }
// }
class A {
  constructor(public name: string, private age: number){
  }
  get _age(){
    return this.age
  }
}
console.log(new A('张三', 10));
console.log(new A('张三', 10)._age);

interface 接口

  • 接口用来定义类的结构
  • 接口可以当做类型来使用,可以进行类型声明
  • 接口里的方法都是抽象方法
  • 接口可以重复声明,相同接口会合并属性和方法
  • 接口定义类的时候,使用implements关键字实现接口,类必须满足接口定义的结构
(function () {
  type myType = { // 使用type定义类型,不能重复定义相同的type类,会报错
    name: string,
    age: number
  }
  //  type myType = {
  //   hobby: Array<string>
  //  }
  interface myInterface {
    name: string;
    age: number;
    say():void // 抽象方法
  }
  interface myInterface { // 多次声明相同接口不会报错
    hobby: string[];
  }
  // interface接口:定义类型声明
  const obj: myType = {name: '张三', age: 18}
  let obj2: myInterface
  obj2 = {
    name: '张三',
    age: 18,
    say() {
      console.log('hi~');
    },
    hobby: ['打游戏']
  }
  // interface接口:定义类结构
  class Person implements myInterface {
    name: string;
    age: number;
    hobby: Array<string>;
    constructor(name: string, age: number, hobby: Array<string>) {
      this.name = name
      this.age = age
      this.hobby = hobby
    };
    say (): void {
      console.log('hi~');
    }
  }
  const p = new Person('张三', 18, ['健身'])
  console.log(p);
  
})()

generic泛型

  • 当遇到类型不明确,无法确定其类型的时候,此时就可以使用泛型
  • 泛型名字可以随便起
  • 在函数中
    function fn<T>(a: T): T{
      return a;
    }
    // 多个泛型
    function fn2<T, K>(a: T, b: K): T{
      console.log(b);
      return a;
    }
    // 1. 直接调用
    console.log(fn(1));
    // 2. 指定泛型类型
    console.log(fn<string>('hello'));
    // 3. 指定多个泛型
    console.log(fn2<number, string>(10, 'hello'));
  • 在class类中
    class D<T>{
      name: T;
      constructor(name: T){
        this.name = name
      }
    }
    console.log(new D<string>('张三').name);
  • 泛型范围约束

    • 使用interface接口约束
    • extends使泛型继承interface接口
    // 约束泛型范围 必须要有一个属性 age
    interface inter{
      age: number
    }
    
    function F<T extends inter>(a: T) {
      return a
    }
    F({age: 10})
1
选择打赏方式:
微信

评论 (0)

取消