TypeScript简介以及使用

2022-10-281947

TypeScript

前言

javascript是一门脚本语言(直接运行,无编译阶段), 无类型系统,在运行时才知道变量类型,才发现一些隐藏的错误

  • 优点
    • 灵活
    • 无需编译
    • 跨平台
  • 缺点
    • 太灵活
    • 无编译阶段
    • 难以维护
    • 编写代码时无提示
    • 无类型系统
  • 类型系统
    • 从类型安全角度:强类型与弱类型 是否能进行隐式类型转换,是否允许形参与实参的不同
    • 从类型检查角度:静态类型与动态类型 是否允许修改变量类型 结论:javascript是一门弱类型且动态类型语言

介绍

TypeScript由微软开发的自由和开源的编程语言,设计目标是开发大型应用,是js的扩展,在js的基础上增加了类型系统及ES6+

官网:https://www.tslang.cn

特点

  • 静态类型:解决了开发者不小心修改了字段类型/字段个数,而导致项目出现问题的痛点
  • 支持ECMAScript所有特性:始于JavaScript,归于JavaScript
  • 丰富的配置选项:通过配置选项来规避一些隐藏问题和安全隐患
  • 强大的工具支持:解决了IDE/编辑器无法智能提示的痛点

浏览器支持情况

浏览器不支持TS,需要通过编译成JS后才能在浏览器中运行

环境安装

ts基于NodeJS,所以先确认安装了node环境,然后通过npm安装 typescript

全局安装

全局安装后可在命令行直接编译typescript

    npm install -g typescript

    # 安装后通过tsc命令执行
    tsc -v

    # 编译:在当前目录下生成helloword.js文件
    tsc helloworld.ts

项目安装

需要生成tsconfig.json文件

    npm install typescript
  • 编译
    • 命令行运行tsc
    • 配合webpack + ts-loader等工具实现编译

编译成功后报错 无法重新声明块范围变量“message”。

之所以 tslint 会提示这个错误,是因为在 Commonjs 规范里,没有像 ESModule 能形成闭包的「模块」概念,所有的模块在引用时都默认被抛至全局,因此当再次声明某个模块时,TypeScript 会认为重复声明了两次相同的变量进而抛错。

对于这个问题,最简单的解决方法是在报错的文件底部添加一行代码:export {}。这行代码会「欺骗」tslint 使其认为当前文件是一个 ESModule 模块,因此不存在变量重复声明的可能性。当使用这个方法时,记得这样配置你的 tsconfig.json 文件:

使用

类型注解和类型检查

username:string 这一行为是 类型注解

一种轻量级的为变量或函数添加约束的方式(把js中的变量动态类型变成静态类型),格式:var 变量名:类型 = 值

  • 基本类型

        let username:string = 'laoxie';
        let age:number = 18;
        let isLogin:boolean = true
    
        // username = 123456; //在编译时报错,因为username已经确定为string字符串类型
  • 特殊类型

    • any:任意类型,所有值可赋值给any变量(相当前关闭ts的类型检查) 变量如果在声明的时候,未指定其类型,那么它会被自动识别为any类型
    • unknown 类似于any,所有值可赋值给unknown变量,但反过来不行
    • void: 空值,表示没有任何类型,一般用于函数返回值 当一个函数没有返回值时,可以设置为 void
    • never: 永不存在的值的类型,一般用于函数返回值 一般用于抛出异常或出现死循环时的函数
    • null
    • undefined
    • 字面量:等效于常量 固定值做为变量类型,一般很少用
  • 联合类型 表示取值可以为多种类型中的一种

        let age:number|string = 18;
        let age:number|string = '18';
  • 类型别名 使用type关键字给类型指定一个新的名字

        type ageType = number|string
        let age:ageType = 18;
  • 类型推论 根据赋值的数据自动推断变量类型

    • 未赋值:推论为any类型(不对类型进行检查)
    • 赋值:推论为值所属类型 PS: 虽然ts能自动推断出变量的值,但在实际开发过过程中还是建议指定变量类型,这样更直观,也更容易读懂我们的代码
  • 函数类型

    • 声明式函数 需要指定参数类型与返回值类型

          function getData(url:string,page:number):number{
              // ajax请求
              return page;
          }
          getData('/list');//报错,缺少参数
          getData('/list',1,'get')// 报错,多余参数
    • 表达式函数 函数表达式除了指定变量的类型,也需要指定函数的类型

          let getData:(url:string,page:number)=>void = function(url:string,page:number):void{
              // ajax请求
          }
      
          // 使用接口指定类型
          interface IgetData{
              (url:string,page:number):void
          }
          let getData:IgetData = function(url:string,page:number):void{
              // ajax请求
          }
    • 可选参数 可选参数必须作为最后一个形参

          function getData(url:string,page?:number):void{
              // ajax请求
          }
    • 默认参数 默认参数与可选参数不能同时设置

          function getData(url:string,page:number=1,type:string='get'):void{
              // ajax请求
          }
    • 剩余参数

          function getData(...rest:string[]):void{
              // ajax请求
          }
  • 数组类型

    • 类型+[]

    • 泛型

    • 接口

        let arr:number[] = [10,20,30]
        let arr:Array<string> = ['laoxie','lemon','jingjing']
      
        interface IGoods{
            name:string,
            price:number
        }
        interface IGoodslist{
            [index:number]:IGoods
        }
        let arr:IGoods[] = [{name:'iphone',price:998}]
        let arr:Array<IGoods> = [{name:'iphone',price:998}]
        let arr:IGoodslist = [{name:'iphone',price:998}]
  • 元组Tuple 类似于数组,是一个明确元素数量和类型的数组,各元素的类型不必相同,一般用于函数返回值

        let arr:[number,number,string] = [10,20,'h5']
  • 枚举类型enum 支持数字(默认)的和字符串的枚举

        // 数字枚举成员的值从0开始自动递增(也可以手动设置)
        enum Status {
            Error,
            Success,
            warning
        }
    
        // 字符串枚举不能自动递增,必须为每个成员赋值
        enum Gender {
            Male = '男',
            Female = '女',
            Unno = '未知'
        }
  • 接口Interface 接口用于描述对象的形状(确定对象的属性结构)

    • ?: 可选属性

    • readonly: 只读属性

    • any: 任意属性

        interface Person {
            name: string;
      
            // 可选属性
            age?:number;
      
            // 只读属性(只能在创建的时候被赋值)
            readonly marry:boolean
      
            // 任意属性(一般不用)
            [propName: string]: any;
      
            // 方法定义
            say():void;
            getInfo:()=>string
        }
      
        let user:Person = { name: "laoxie",marry:true};
  • 类型断言: 当ts解析器不确定某个变量类型时,我们可以as告诉解析器它的类型

    • 尖括号语法:<类型>变量

    • as语法:变量 as 类型

        let arr:number[] = [10,20,30]
        let num = arr.find(item=>item>10);
      
        // 因为number可能得到number或undefined,所以会报:Object is possibly 'undefined' 的错误
        let res = num + 1; 
      
        // 可以用断言写法解决
        let res = num as number +1
        let res = <number>num +1
  • 泛型编程 在定义函数、接口或类时不明确类型,可以使用泛型,可以让编写的代码更加灵活。泛型即定义时不指定具体类型,而是使用类型变量,调用时才指定类型的编程方式,这样能更好的实现代码复用

        function createArray<T>(item: T, len: number): T[] {
            return Array(len).fill(item)
        }
        let arr1 = createArray(100,3); // T会自动推断为number
        let arr22 = createArray<string>("myString",5); // 指定定泛型
    
        class Person<T>{
            name:T;
            constructor(name:T){
                this.name = name
            }
        }
        const p = new Person<string>('laoxie')
  • 函数重载

        function add(a: number, b: number): number;
        function add(a: string, b: string): string;
        function add(a: any, b: any): any {
            return a + b;
        }
        add(10, 20);
        add('10', '20');
        // add('10',20); // 报错
    • 定义class 与ES6一致

    • 继承extends 与ES6一致

    • 修饰符

      • public:公有属性,可以在任何地方被访问到 默认所有的属性和方法都是 public 的
      • private:私有属性,只能在当前类内部访问
      • protected:受保护的属性,它和 private 类似,区别是它在子类中也是允许被访问的
      • readonly:只读属性关键字,只允许属性声明(与其他修饰符一起使用时必须写在最后)
      • static: 静态属性,只能通过类属性
    • 属性类型检查 与接口一致

      class Student {
          // 实例属性
          fullName: string;
          // 静态属性
          static age = 18;
      
          // public firstName
          // 在构造函数参数中使用修饰符,等效于以上写法
          constructor(public firstName, public middleInitial, public lastName) {
              this.fullName = firstName + " " + middleInitial + " " + lastName;
          }
      }
      
      interface Person {
          firstName: string;
          lastName: string;
      }
      
      function greeter(person : Person) {
          return "Hello, " + person.firstName + " " + person.lastName;
      }
      
      let user = new Student("Jane", "M.", "User");
      
      document.body.innerHTML = greeter(user);
  • 命名空间namespce(了解) namespce目的就是解决重名问题,可以利用同一个命名空间把代码分散到不同的文件

        namespace Validation {
            export interface StringValidator {
                isAcceptable(s: string): boolean;
            }
        }
        // 使用步骤:
        // 1. 引用文件(三个斜杠)
        /// <reference path="Validation.ts"/>
        // 2. 通过点语法调用
        Validation.StringValidator;

    PS:以上命名空间最终会被声明为全局对象,所以在typescript中已经不推荐使用(后续版本会被移除),建议使用Module来组织代码结构

        // module/validate.js
        export namespace Validation {
            export interface StringValidator {
                isAcceptable(s: string): boolean;
            }
        }
    
        // 使用步骤:
        // 1. 引入
        import {Validation} from './module/validate.js'
        // 2. 通过点语法调用
        Validation.StringValidator;
  • 模块

    • CommonJS(默认)
    • ES Module 与ES Module一致

编译typescript

命令行编译

  • 命令格式:tsc <文件> <参数>
    • 编译文件:tsc 文件.ts
    • 监听文件:tsc 文件.ts -w
    • 监听所有文件:tsc 需要tsconfig.json配置文件
  • 参数:
    • --help 显示帮助信息
    • --init 初始化ts项目及创建一个tsconfig.json文件
    • --watch 在监视模式下运行编译器。当被监听的文件发生改变时自动重新编译。
    • --module 指定输出代码使用的模块规范
    • --target 指定编译 ECMAScript 目标版本
    • --declaration 生成一个 .d.ts 类型声明文件。
          # 以下命令会生成 test.d.ts、test.js 两个文件
          tsc test.ts --declaration
    • --sourcemap 生成一个 sourcemap (.map) 文件。 sourcemap 是一个存储源代码与编译代码对应位置映射的信息文件。
    • --local zh-CN 显示中文错误提示
    • --removeComments 删除文件的注释
    • --outFile 编译多个文件并合并到一个输出的文件
    • --outDir 编译js文件到指定目录(默认为ts文件所有目录)
    • --noImplicitAny 在表达式和声明上有隐含的 any 类型时报错

编译工具编译

  • 依赖
    • webpack
    • webpack-cli
    • typescript
    • ts-loader
  • webpack加载器:ts-loader 依赖typescript与 tsconfig.json配置
        {
            //...
            module: {
                rules: [
                    { test: /\.tsx?$/, loader: "ts-loader" }
                ]
            }
        }
  • 在react项目中使用ts
    • 手动配置
      • 安装类型声明文件
        • @types/react
        • @types/react-dom
      • 文件名使用.tsx ts文件中不支持编写JSX
      • tsconfig.json中设置jsx:'react'
      • webpack配置中添加.tsx到resolve.extensions
    • 命令行工具创建项目
          #CRA
          create-react-app <project> --template typescript

在线编译

https://www.typescriptlang.org/play/

类型声明文件(.d.ts)

tsconfig.json配置参考

通过 tsc --init 生成,配置文件只有编译项目(tsc)时才生效,tsc编译具体文件(tsc xx.ts)不会生效

  • files - 编译的文件的名称

  • include - 设置需要进行编译的文件

  • exclude - 设置排除编译的文件

  • compilerOptions - 编译选项

    "compilerOptions": {
    
        /* 基本选项 */
        "target": "es5",                       // 指定编译 ECMAScript 目标版本: ES3 (默认), ES5, ES6/ES2015, ES2016, ES2017, ESNEXT
        "module": "commonjs",                  // 指定输出代码使用的模块规范: commonjs, amd, system, umd or es2015
        "lib": [],                             // 指定要包含在编译中的库文件
        "allowJs": true,                       // 是否允许编译 js 文件
        "checkJs": true,                       // 是否检查 js 文件中的错误
    
        "jsx": "preserve",                     // 指定 jsx 代码的生成: 'preserve', 'react-native', 'react'
        // * 在 preserve 模式下生成代码中会保留 JSX 以供后续的转换操作使用(比如:Babel)。 另外,输出文件会带有 .jsx 扩展名。
        // * react 模式会生成 React.createElement,在使用前不需要再进行转换操作了,输出文件的扩展名为.js。
        // * react-native 相当于 preserve,它也保留了所有的 JSX,但是输出文件的扩展名是.js。
    
        "declaration": true,                   // 是否生成相应的 '.d.ts' 文件
        "sourceMap": true,                     // 是否生成相应的 '.map' 文件(源码映射文件,一般用于调试)
        "outFile": "./",                       // 将输出文件合并为一个文件
        "outDir": "./",                        // 指定编译后的js文件输出目录
        "rootDir": "./",                       // 用来控制输出目录结构 --outDir.
        "removeComments": true,                // 是否删除编译后的注释
        "noEmit": true,                        // 不生成输出文件
        "importHelpers": true,                 // 从 tslib 导入辅助工具函数
        "isolatedModules": true,               // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似).
    
        /* 严格的类型检查选项 */
        "strict": true,                        // 启用所有严格类型检查选项
        "noImplicitAny": true,                 // 在表达式和声明上有隐含的 any类型时报错
        "strictNullChecks": true,              // 启用严格的 null 检查
        "noImplicitThis": true,                // 当 this 表达式值为 any 类型的时候,生成一个错误
        "alwaysStrict": true,                  // 以严格模式检查每个模块,并在每个文件里加入 'use strict'
    
        /* 额外的检查 */
        "noUnusedLocals": true,                // 有未使用的变量时,抛出错误
        "noUnusedParameters": true,            // 有未使用的参数时,抛出错误
        "noImplicitReturns": true,             // 并不是所有函数里的代码都有返回值时,抛出错误
        "noFallthroughCasesInSwitch": true,    // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)
    
        /* 模块解析选项 */
        "moduleResolution": "node",            // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
        "baseUrl": "./",                       // 用于解析非相对模块名称的基目录
        "paths": {},                           // 模块名到基于 baseUrl 的路径映射的列表
        "rootDirs": [],                        // 根文件夹列表,其组合内容表示项目运行时的结构内容
        "typeRoots": [],                       // 包含类型声明的文件列表
        "types": [],                           // 需要包含的类型声明文件名列表
        "allowSyntheticDefaultImports": true,  // 允许从没有设置默认导出的模块中默认导入。
    
        /* 其他选项 */
        "sourceRoot": "./",                    // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
        "mapRoot": "./",                       // 指定调试器应该找到映射文件而不是生成文件的位置
        "inlineSourceMap": true,               // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
        "inlineSources": true,                 // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性
    
        "experimentalDecorators": true,        // 启用装饰器
        "emitDecoratorMetadata": true          // 为装饰器提供元数据的支持
    }
分享
点赞0
打赏
上一篇:代理工具Fiddler -调试与替换接口状态
下一篇:threejs简单入门