TypeScript__第一章

291 阅读25分钟

第一章 快速入门

0、TypeScript简介

1. TypeScript是JavaScript的超集。
2. JS的变量是动态类型的,随时可被改变为各种类型。TypeScript对JS进行了扩展,
向JS中引入了类型的概念,并添加了许多新的特性。
3. 浏览器不认识TS,TS不能被JS解析器直接执行,TS代码需要通过编译器编译为JS,
然后再交由JS解析器执行。
4. TS完全兼容JS,换言之,任何的JS代码都可以直接当成JS使用。
5. 相较于JS而言,TS拥有了静态类型,更加严格的语法,更强大的功能;TS可以在代码
执行前就完成代码的检查,减小了运行时异常的出现的几率;TS代码可以编译为任意版本
的JS代码,可有效解决不同JS运行环境的兼容问题;同样的功能,TS的代码量要大于JS,
但由于TS的代码结构更加清晰,变量类型更加明确,在后期代码的维护中TS却远远胜于
JS。

1、TypeScript 开发环境搭建

  1. 下载Node.js

  2. 安装Node.js

  3. 使用npm全局安装typescript编译器

    • 进入命令行
    • 输入:npm i -g typescript
  4. 创建一个ts文件

  5. 使用tsc对ts文件进行编译

    • 进入命令行

    • 进入ts文件所在目录

    • 执行命令:tsc xxx.ts

2、基本类型

  • 类型声明

    • 类型声明是TS非常重要的一个特点

    • 通过类型声明可以指定TS中变量(参数、形参)的类型

    • 指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错

    • 简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值

    • 语法:

      • let 变量: 类型;
        
        let 变量: 类型 = 值;
        
        function fn(参数: 类型, 参数: 类型): 类型{
            ...
        }
        
  • 案例汇总__类型声明

    •   // 声明一个变量a,同时指定它的类型为number
          let a: number;
      
          // a 的类型设置为了number,在以后的使用过程中a的值只能是数字
          a = 10;
          a = 33;
          // a = 'hello'; // 此行代码会报错,因为变量a的类型是number,
          //不能赋值字符串
          let b: string;
          b = 'hello';
          // b = 123;// 此行代码会报错,因为变量b的类型是string
      
          // 声明完变量直接进行赋值
          // let c: boolean = false;
      
          // 如果变量的声明和赋值是同时进行的,TS可以自动对变量进行类型检测
          let c = false;
          c = true;
      
          // JS中的函数是不考虑参数的类型和个数的
          // function sum(a, b){
          //     return a + b;
          // }
      
          // console.log(sum(123, 456)); // 579
          // console.log(sum(123, "456")); // "123456"
      
          function sum(a: number, b: number): number{
          return a + b;
          }
      
          let result = sum(123, 456);
          let result = sum(123, "456");//报错
      
  • 自动类型判断

    • TS拥有自动的类型判断机制
    • 当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型
    • 所以如果你的变量的声明和赋值时同时进行的,可以省略掉类型声明
  • typeof 和 keyof 和 in

    • typeof 操作符可以用来获取一个变量声明或对象的类型。
    function toArray(x: number): Array<number> {
        return [x];
    }
    
    type Func = typeof toArray; // -> (x: number) => number[]
    
    • keyof 操作符可以用来一个对象中的所有 key 值:
    interface Person {
        name: string;
        age: number;
    }
    
    type K1 = keyof Person; // "name" | "age"
    
    • in 用来遍历枚举类型:
    type Keys = "a" | "b" | "c"
    
    type Obj =  {
      [p in Keys]: any
    } // -> { a: any, b: any, c: any }
    
  • 类型守卫,语法规范范围内,额外的确认

    • 多态 - 多种状态(多种类型)
    // in - 定义属性场景下内容的确认
    interface Teacher {
        name: string;
        courses: string[];
    }
    interface Student {
        name: string;
        startTime: Date;
    }
    
    type Class = Teacher | Student;
    
    function startCourse(cls: Class) {
        if ('courses' in cls) {
            console.log("Courses:" + cls.courses);
        }
        if ('startTime' in cls) {
            console.log("startTime:" + cls.startTime);
        }
    }
    
    // typeof / instanceof - 类型分类场景下的身份确认
    function class(name: string, score: string | number) {
        if (typeof score === "number") {
            return "teacher:" + name + ":" + score;
        }
        if (typeof score === "string") {
            return "student:" + name + ":" + score;
        }
    }
    
    const getName = (cls: Class) => {
        if(cls instanceof Teacher) {
            return cls.courses;
        }
        if(cls instanceof Student) {
            return cls.startTime;
        }
    }
    
    // 自定义类型
    const isTeacher = function (cls: Teacher | Student): cls is Teacher {
        return 'courses' in cls;
    }
    
    const getName = (cls: Teacher | Student) => {
        if(isTeacher(cls)) {
            return cls.courses;
        }
    }
    
    // 类型别名 & 联合类型
    
  • extends

    • extends在typescript里的用来继承类型,有时候我们定义的泛型不想过于灵活或者说想继承某些类等,可以通过 extends 关键字添加泛型约束。
    interface ILengthwise {
      length: number;
    }
    
    //意思是:传入的参数必须要有length这个属性
    function loggingIdentity<T extends ILengthwise>(arg: T): T {
      console.log(arg.length);
      return arg;
    }
    
    loggingIdentity(3);//数字3是没有length的,会报错
    loggingIdentity("3");//字符串3是有length属性的,不会报错
    loggingIdentity({length: 10, value: 3});//不会报错
    
  • 联合类型 | 、 交叉类型 &

    • 联合类型 | ,满足其一即可

    • 交叉类型 & ,这样所有类型会合并

    // 交叉类型 &   合并
    interface A {
        inner: D;
    }
    interface B {
        inner: E;
    }
    interface C {
        inner: F;
    }
    
    interface D {
        d: boolean;
    }
    interface E {
        e: string;
    }
    interface F {
        f: number;
    }
    
    type ABC = A & B & C;
    
    let abc: ABC = {
        inner: {
            d: false,
            e: 'className',
            f: 5
        }
    };
    
    // 交叉类型 &   合并冲突
    interface A {
        c: string;
        d: string;
    }
    interface B {
        c: number;
        e: string;
    }
    
    type AB = A & B;
    
    let ab: AB;
    // 合并的关系是'且' => c - never 
    // 所以不建议合并冲突
    
  • 高级类型,即一些ts自己封装的类型

    • Paritial:Partial 的作用就是将某个类型里的属性全部变为可选项 ?。
    • Reuqired:Required 的作用就是将某个类型里的属性全部变为必选项。
    • Readonly:Readonly 的作用是将某个类型所有属性变为只读属性,也就意味着这些属性不能被重新赋值。
    • Record:Record<K extends keyof any, T> 的作用是将 K 中所有的属性的值转化为 T 类型。
    • Exclude:Exclude<T, U> 的作用是将某个类型T中属于另一个的类型U移除掉。
    • Extract:Extract<T, U> 的作用是从 T 中提取出 U,就像取交集。
    interface PageInfo {
      title: string;
    }
    
    type OptionalPageInfo1 = Paritial<PageInfo>
    //这样OptionalPageInfo1就变成了:
    //interface PageInfo {
    //  title?: string;
    //}
    
    type OptionalPageInfo2 = Readonly<PageInfo>
    const pageInfo: OptionalPageInfo2 = {title: ""};
    pageInfo.title = "111"//会报错,类型层面的报错
    
    // Readonly与JS的引用类型操作不同,常与const做对比 
    const shuzu = [6, 7, 8] ;
    shuzu.push(9);      //OK
    
    let arr: number[] = [1, 2, 3, 4];
    let ro: ReadonlyArray<number> = arr;
    ro[0] = 12;         // 赋值 - Error
    ro.push(5);         // 增加 - Error
    ro.length = 10;     // 长度改写 - Error
    arr = ro;           // 覆盖 - Error
    
    
    type Page = "home" | "about" | "contact";
    
    const x: Record<Page, PageInfo> = {
      about: { title: "xxx" },
      contact: { title: "xxx" },
      home: { title: "xxx" }
    };//about,contact,home对应键值是字符串类型
    
    type T0 = Exclude<"a" | "b" | "c", "a">; // 只剩"b" | "c"
    type T1 = Exclude<"a" | "b" | "c", "a" | "b">; //只剩 "c"
    
    type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
    type T1 = Extract<string | number | (() => void), Function>; // () => void
    
    
    
  • 任意可添加属性

    // 任意可添加属性
    interface Class {
        readonly name: string;
        time: number;
        [propName: string]: any;
    }
    
    const c1 = { name: "JS" };
    const c2 = { name: "browser", time: 1 };
    const c3 = { name: "ts", level: 1 };
    
  • 断言 - 类型的声明和转换(开发者和编译器做了一个告知交流)

    • 编译时作用
    // 尖括号形式
    let anyValue: any = 'hi zhaowa';
    let anyLength: number = (<string>anyValue).length;
    
    // as声明
    let anyValue: any = 'hi zhaowa';
    let anyLength: number = (anyValue as string).length;
    
    // 非空判断 - 只确定不是空
    type ClassTime = () => number;
    
    const start = (ClassTime: ClassTime | undefined) {
        // 业务逻辑
        // if (额外判断逻辑) {
        // 注意感叹号,具体类型待定,但是非空确认
            let time = classTime!(); 
        // }
    }
    
    // 面试题
    const tsClass: number | undefined = undefined;
    const zhaowa: number = tsClass!;
    console.log(zhaowa);
    
    // 面试题其实会转义成
    const tsClass = undefined;
    const zhaowa = tsClass;
    console.log(zhaowa); // undefined
    
    // 肯定断言 - 肯定化保证赋值
    let score: number;
    startClass();
    console.log('' + score); // 使用前赋值
    
    function startClass() {
        score = 5;
    }
    
    // let score!: number; - 提前告知
    
  • declare module

    //因为vue中,import APNG from "a.png",是不行的,
    //.png默认不是模块,需要declare module来说、声明它是模块,
    //让vue认为它可以被导入
    //所以,以后有什么无法被当成模块,而你需要引入它的时候,想起这个方法
    declare module '*.json' {
        const value: any;
        export default value;
    }
    
    declare module '*.vue' {
        import Vue from 'vue';
        export default Vue;
    }
    
    declare module '*.png';
    
    declare module '*.jpg';
    
    declare module '*.jpeg';
    
    declare module '*.gif';
    
    declare module '*.svg';
    //备注:vscode显示包大小的插件--import cost 
    
    
  • 类型:

    类型例子描述
    number1, -33, 2.5任意数字
    string'hi', "hi", hi任意字符串
    booleantrue、false布尔值true或false
    字面量其本身限制变量的值就是该字面量的值
    any*任意类型
    unknown*类型安全的any
    void空值(undefined)没有值(或undefined)
    never没有值不能是任何值
    object{name:'孙悟空'}任意的JS对象
    array[1,2,3]任意JS数组
    tuple[4,5]元组,TS新增类型,固定长度数组
    enumenum{A, B}枚举,TS中新增类型
  • number

let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let big: bigint = 100n;
  • boolean
let isDone: boolean = false;
  • string
let color: string = "blue";
color = 'red';

let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}.

I'll be ${age + 1} years old next month.`;
  • 字面量
// 也可以使用字面量去指定变量的类型,通过字面量可以确定变量的取值范围
let color: 'red' | 'blue' | 'black';
let num: 1 | 2 | 3 | 4 | 5;
  • any
let d: any = 4;
d = 'hello';
d = true;
  • unknown
let notSure: unknown = 4;
notSure = 'hello';
  • void
let unusable: void = undefined;
  • never
function error(message: string): never {
    throw new Error(message);
}
  • object(没啥用)
let obj: object = {};
  • array
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
  • tuple
//长度固定的数组,数组里可以有不同的数据类型
let x: [string, number];
x = ["hello", 10]; 
  • enum
    • 数字型枚举、字符串型枚举、反向映射、异构
// 数字型枚举,没给值,默认从0开始,依次递增
enum Color {
    RED,//0
    GREEN,//1
    BLUE,//2
}
let c: Color = Color.GREEN;

//字符串型枚举
enum Color {
    RED = "RED",
    GREEN = "GREEN",
    BLUE = "BLUE",
}
let c: Color = Color.GREEN;

enum Color {
    RED = 1,
    GREEN = 2,
    BLUE = 4,
}
let c: Color = Color.GREEN;

let temp = Gender['RED'] //反向映射,结果为 RED 

// 异构 
enum Enum { 
    A, // 0 
    B, // 1 
    C = 'C', 
    D = 'D', 
    E = 8, 
    F, // 9 以上一个数值为依据而得出9
}


  • 案例汇总__类型
//typescriptPractice.js文件
// 也可以直接使用字面量进行类型声明
let a: 10;
a = 10;

// 可以使用 | 来连接多个类型(联合类型)
let b: "male" | "female";
b = "male";
b = "female";

let c: boolean | string;
c = true;
c = 'hello';

// any 表示的是任意类型,一个变量设置类型为any后相当于对该变量
// 关闭了TS的类型检测,绕过所有类型检查=>类型检测和编译筛查取消
// 使用TS时,不建议使用any类型,
let d: any;
let anyValue: any = 123;

anyValue = "anyValue";
anyValue = false;
let value1: boolean = anyValue;

// 声明变量如果不指定类型,
//则TS解析器会自动判断变量的类型为any (隐式的any)。
let d;
d = 10;
d = 'hello';
d = true;

// unknown 表示未知类型的值
// 绕过赋值检查 => 禁止更改传递
let e: unknown;
e = 10;
e = "hello";
e = true;

// d的类型是any,它可以赋值给任意变量,这一点非常不好,
// 相当于改变了对方s的类型,但是unknown不能直接赋值给其他变量
let s:string;
s = d;

// unknown 实际上就是一个类型安全的any
// unknown类型的变量,不能直接赋值给其他变量
unknownValue = true;
unknownValue = 123;
unknownValue = "unknownValue";

let value1: unknown = unknownValue; // OK
let value2: any = unknownValue;  // OK
let value3: boolean = unknownValue; // NOK,不OK

// 类型断言,可以用来告诉解析器变量的实际类型
/*
* 语法:
* 变量 as 类型
* <类型>变量
*
* */
s = e as string;
s = <string>e;

// void 用来声明返回为空,以函数为例,就表示没有返回值的函数
function fn(): void{
}

//不指定返回类型,也没有写返回值,默认void返回值
 function fn(){
}

//不指定返回类型,写了返回值,即根据返回值来定返回类型
 function fn(num){
     if(num>0) {
         return true;
     }else{
         return 123;
     }
}//此时返回类型是true | 123

// never 表示永不能执行完 or 永远error
function errorGen(msg: string): never {
    throw new Error(msg);
}

function infiniteLoop(): never {
    while(true) {
        // 业务逻辑
    }
}

// object表示一个js对象,没啥用
// 分成,object / Object / {} - 对象,三步来学习
// object - 用来描述非原始类型的、后天创建的类型约束
interface ObjectConstructor {
    create(o: object | null): any;
}

const proto = {};

Object.create(proto);     // OK
Object.create(null);      // OK
Object.create(undefined); // Error

// Object
// Object.prototype 上的属性
interface Object {
    constructor: Function;
    toString(): string;
    toLocaleString(): string;
    valueOf(): Object;
}

// 定义了Object类属性
interface ObjectConstructor {
    new(value: any): Object;
    readonly prototype: Object;
}

// {} - 定义空属性对象
const obj = {};

obj.prop = "props"; // 不存在这个属性,所以NOK
obj.toString(); // toString是Object上的方法,所以OK




let a: object;
a = {};
a = function () {
};

// {} 用来指定对象中可以包含哪些属性
// 语法:{属性名:属性值,属性名:属性值}
// 在属性名后边加上?,表示属性是可选的
let b: {name: string, age?: number};
b = {name: '孙悟空', age: 18};

// [propName: string]: any 表示任意类型的属性
let c: {name: string, [propName: string]: any};
//这样你这可以在后面任意加键值对,不限类型,propName写其他字符串名字都行
c = {name: '猪八戒', age: 18, gender: '男'};//这样你这可以在后面任意加
let C: {name: string, [xxx: string]: any};//xxx也行

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


/*
*   数组的类型声明:
*   类型[]
*   Array<类型>
* */
// string[] 表示字符串数组
let e: string[];
e = ['a', 'b', 'c'];

// number[] 或者 Array<number> 均可表示数值数组
let f: number[];

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

/*
*   元组 tuple ,元组就是固定长度的数组
*   长度固定的数组,数组里可以有不同的数据类型
*   语法:[类型, 类型, 类型]
* */
let h: [string, number];
h = ['hello', 123];

/*
* enum 枚举,可以理解为常量的集合
* 建议全大写,以_隔开
* 数字型枚举,没给值,默认从0开始,依次递增
* 字符串型枚举
* 反向映射
* 异构
* */
enum Gender{//定义一个数字型枚举,没给值,默认从0开始,依次递增
    MALE,   //0
    FEMALE  //1
}

let temp = Gender[1]  //反向映射,结果为 FEMALE

enum Gender{//定义一个字符串型枚举
    MALE = 'male',
    FEMALE = 'female'
}

let temp = Gender['female']  //反向映射,结果为 FEMALE

// 异构
enum Enum {
    A,        // 0
    B,        // 1
    C = 'C',
    D = 'D',
    E = 8,
    F,        // 9,以上一个数值为依据而得出9
}

export enum Gender{//也可以export
    MALE = 'male',
    FEMALE = 'female'
}

let i: {name: string, gender: Gender};
i = {
    name: '孙悟空',
    gender: Gender.MALE // 'male'
}

// console.log(i.gender === Gender.FEMALE);
// &表示同时,都要满足
let j: { name: string } & { age: number };
// j = {name: '孙悟空', age: 18};

// 类型的别名 type
let k: 1 | 2 | 3 | 4 | 5;
let l: 1 | 2 | 3 | 4 | 5;//此行没有考虑复用性

type myType = 1 | 2 | 3 | 4 | 5;
let k: myType;
let l: myType;
let m: myType;

k = 2;
type K1 = keyof Person; // "name" | "age"

type User = {
  name: string
  age: number
};

/*
* typeof 操作符可以用来获取一个变量声明或对象的类型。
* */
function toArray(x: number): Array<number> {
  return [x];
}
type Func = typeof toArray; // -> (x: number) => number[]

 /*
* keyof 操作符可以用来一个对象中的所有 key 值
* */
interface Person {
    name: string;
    age: number;
}

/*
* in 用来遍历枚举类型
* */
type Keys = "a" | "b" | "c"
type Obj =  {
  [p in Keys]: any
} // -> { a: any, b: any, c: any }




//在index.html里引入该js文件,查看结果
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="typescriptPractice.js"></script>
</body>
</html>
  • 类型断言

    • 有些情况下,变量的类型对于我们来说是很明确,但是TS编译器却并不清楚,此时,可以通过类型断言来告诉编译器变量的类型,断言有两种形式:

第一种

let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;

第二种

let someValue: unknown = "this is a string";
let strLength: number = (<string>someValue).length;

3、编译选项

  • 自动编译文件

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

    • 示例:

      • tsc xxx.ts -w
        
  • 自动编译整个项目

    • 如果直接使用tsc指令,则可以自动将当前项目下的所有ts文件编译为js文件。

    • 但是能直接使用tsc命令的前提时,要先在项目根目录下创建一个ts的配置文件 tsconfig.json

    • tsconfig.json是一个JSON文件,添加配置文件后,只需只需 tsc 命令即可完成对整个项目的编译

    • 配置选项:

      • include

        • 定义希望被编译文件所在的目录

        • 默认值:["**/*"]

        • 示例:

          • "include":["src/**/*", "tests/**/*"]
            
          • 上述示例中,所有src目录和tests目录下的文件都会被编译

      • exclude

        • 定义需要排除在外的目录

        • 默认值:["node_modules", "bower_components", "jspm_packages"]

        • 示例:

          • "exclude": ["./src/hello/**/*"]
            
          • 上述示例中,src下hello目录下的文件都不会被编译

      • extends

        • 定义被继承的配置文件

        • 示例:

          • "extends": "./configs/base"
            
          • 上述示例中,当前配置文件中会自动包含config目录下base.json中的所有配置信息

      • files

        • 指定被编译文件的列表,只有需要编译的文件少时才会用到

        • 示例:

          • "files": [
                "core.ts",
                "sys.ts",
                "types.ts",
                "scanner.ts",
                "parser.ts",
                "utilities.ts",
                "binder.ts",
                "checker.ts",
                "tsc.ts"
              ]
            
          • 列表中的文件都会被TS编译器所编译

        • compilerOptions

          • 编译选项是配置文件中非常重要也比较复杂的配置选项

          • 在compilerOptions中包含多个子选项,用来完成对编译的配置

            • 项目选项

              • target

                • 设置ts代码编译的目标版本

                • 可选值:

                  • ES3(默认)、ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext
                • 示例:

                  • "compilerOptions": {
                        "target": "ES6"
                    }
                    
                  • 如上设置,我们所编写的ts代码将会被编译为ES6版本的js代码

              • lib

                • 指定代码运行时所包含的库(宿主环境)

                • 可选值:

                  • ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext、DOM、WebWorker、ScriptHost ......
                • 示例:

                  • "compilerOptions": {
                        "target": "ES6",
                        "lib": ["ES6", "DOM"],
                        "outDir": "dist",
                        "outFile": "dist/aa.js"
                    }
                    
              • module

                • 设置编译后代码使用的模块化系统

                • 可选值:

                  • CommonJS、UMD、AMD、System、ES2020、ESNext、None
                • 示例:

                  • "compilerOptions": {
                        "module": "CommonJS"
                    }
                    
              • outDir

                • 编译后文件的所在目录

                • 默认情况下,编译后的js文件会和ts文件位于相同的目录,设置outDir后可以改变编译后文件的位置

                • 示例:

                  • "compilerOptions": {
                        "outDir": "dist"
                    }
                    
                  • 设置后编译后的js文件将会生成到dist目录

              • outFile

                • 将所有的文件编译为一个js文件

                • 默认会将所有的编写在全局作用域中的代码合并为一个js文件,如果module制定了None、System或AMD则会将模块一起合并到文件之中

                • 示例:

                  • "compilerOptions": {
                        "outFile": "dist/app.js"
                    }
                    
              • rootDir

                • 指定代码的根目录,默认情况下编译后文件的目录结构会以最长的公共目录为根目录,通过rootDir可以手动指定根目录

                • 示例:

                  • "compilerOptions": {
                        "rootDir": "./src"
                    }
                    
              • allowJs

                • 是否对js文件编译
              • checkJs

                • 是否对js文件进行检查

                • 示例:

                  • "compilerOptions": {
                        "allowJs": true,
                        "checkJs": true
                    }
                    
              • removeComments

                • 是否删除注释
                • 默认值:false
              • noEmit

                • 不对代码进行编译
                • 默认值:false
              • sourceMap

                • 是否生成sourceMap

                • 默认值:false

            • 严格检查

              • strict
                • 启用所有的严格检查,默认值为true,设置后相当于开启了所有的严格检查
              • alwaysStrict
                • 总是以严格模式对代码进行编译
              • noImplicitAny
                • 禁止隐式的any类型
              • noImplicitThis
                • 禁止类型不明确的this
              • strictBindCallApply
                • 严格检查bind、call和apply的参数列表
              • strictFunctionTypes
                • 严格检查函数的类型
              • strictNullChecks
                • 严格的空值检查
              • strictPropertyInitialization
                • 严格检查属性是否初始化
            • 额外检查

              • noFallthroughCasesInSwitch
                • 检查switch语句包含正确的break
              • noImplicitReturns
                • 检查函数没有隐式的返回值
              • noUnusedLocals
                • 检查未使用的局部变量
              • noUnusedParameters
                • 检查未使用的参数
            • 高级

              • allowUnreachableCode
                • 检查不可达代码
                • 可选值:
                  • true,忽略不可达代码
                  • false,不可达代码将引起错误
              • noEmitOnError
                • 有错误的情况下不进行编译
                • 默认值:false
  • 示例汇总

{
/*
  tsconfig.json是ts编译器的配置文件,ts编译器可以根据它的信息来对代码进行编译
    "include" 用来指定哪些ts文件需要被编译
      路径:** 表示任意目录
            * 表示任意文件
    "exclude" 不需要被编译的文件目录
        默认值:["node_modules", "bower_components", "jspm_packages"]

*/
  //有webpack之后,include都不用配置了
  "include": [
    "./src/**/*"
  ],

//  "exclude": [
//    "./src/hello/**/*"
//  ]

  /*
    compilerOptions 编译器的选项(最重要)
  */
  "compilerOptions": {

    // target 用来指定ts被编译为的ES的版本
    // 'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext'
    "target": "es2015",

    // module 指定要使用的模块化的规范
    // 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'esnext'
    "module": "es2015",

    // lib用来指定项目中要使用的库
    //'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es
    //2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'webworker.iterable', 'scri
    //pthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.r
    //eflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.st
    //ring', 'es2017.intl', 'es2017.typedarrays', 'es2018.asyncgenerator', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', '
    //es2018.regexp', 'es2019.array', 'es2019.object', 'es2019.string', 'es2019.symbol', 'es2020.bigint', 'es2020.promise', 'es2020.s
    //haredmemory', 'es2020.string', 'es2020.symbol.wellknown', 'es2020.intl', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable
    //', 'esnext.intl', 'esnext.bigint', 'esnext.string', 'esnext.promise', 'esnext.weakref'
    //"lib": ["es6", "dom"]

// outDir 用来指定编译后文件所在的目录,有webpack之后,outDir都不用配置了
    "outDir": "./dist",

    // 将代码合并为一个文件
    // 设置outFile后,所有的全局作用域中的代码会合并到同一个文件中
    //"outFile": "./dist/app.js"

    // 是否对js文件进行编译,默认是false,即要进行编译的目录下,不
    //仅有ts文件,也还有js文件的时候,要不要连js文件也编译。
    //"allowJs": true,

    // 是否检查js代码是否符合语法规范,默认是false
    //即要不要用ts的规范来检测js代码,一般和上一个属性allowJs是配套使用
    //"checkJs": true,

    // 是否移除注释  true就移除
    "removeComments": true,

    // 不生成编译后的文件  true的话,你看到编译之后的目录或文件,如dist
    "noEmit": false,

    // 当有错误时不生成编译后的文件
    "noEmitOnError": true,

    // 所有严格检查的总开关,只要这个为true,下面属性全开true,推荐全开
    "strict": true,

    // 用来设置编译后的文件是否使用严格模式,默认false
    "alwaysStrict": true,

    // 不允许隐式的any类型
    "noImplicitAny": true,
    //此时,代码function fn1(a, b) {return a+b}会报错

    // 不允许不明确类型的this
    "noImplicitThis": true,
    //此时,代码function fn2() {alert(this);}会报错
    //要写出function fn2(this: window) {alert(this);}才行

    // 严格的检查空值,
    "strictNullChecks": true
    //即true时,当某些属性存在null空值的风险时,会报错
    //let box1 = document.getElementById('box1');
    //示例:
    // if(box1 !== null){
    //     box1.addEventListener('click', function (){//会报错
    //         alert('hello');
    //     });
    // }
    //box1?.addEventListener('click', function (){//才对
        //alert('hello');
    //});
  }
}
  • 更详细的版本
{
  "compilerOptions": {

    /* 基本选项 */
    "target": "es5",                       // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
    "module": "commonjs",                  // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
    "lib": [],                             // 指定要包含在编译中的库文件
    "allowJs": true,                       // 允许编译 javascript 文件
    "checkJs": true,                       // 报告 javascript 文件中的错误
    "jsx": "preserve",                     // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
    "declaration": true,                   // 生成相应的 '.d.ts' 文件
    "sourceMap": true,                     // 生成相应的 '.map' 文件
    "outFile": "./",                       // 将输出文件合并为一个文件
    "outDir": "./",                        // 指定输出目录
    "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,  // 允许从没有设置默认导出的模块中默认导入。

    /* Source Map Options */
    "sourceRoot": "./",                    // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
    "mapRoot": "./",                       // 指定调试器应该找到映射文件而不是生成文件的位置
    "inlineSourceMap": true,               // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
    "inlineSources": true,                 // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性

    /* 其他选项 */
    "experimentalDecorators": true,        // 启用装饰器
    "emitDecoratorMetadata": true          // 为装饰器提供元数据的支持
  }
}

4、webpack

  • 通常情况下,实际开发中我们都需要使用构建工具对代码进行打包,TS同样也可以结合构建工具一起使用,下边以webpack为例介绍一下如何结合构建工具使用TS。

  • 步骤:

    1. 初始化项目

      • 进入项目根目录,执行命令 npm init -y
        • 主要作用:创建package.json文件
    2. 下载构建工具

      • npm i -D webpack webpack-cli webpack-dev-server typescript ts-loader html-webpack-plugin clean-webpack-plugin
        • 共安装了7个包
          • webpack
            • 构建工具webpack
          • webpack-cli
            • webpack的命令行工具
          • webpack-dev-server
            • webpack的开发服务器,项目会在这个内置服务器上运行,自动拉起页面,webpack与这个服务器自动关联,会根据你的项目自动帮你去刷新。别忘了安装完,记得在package.json里加【"start": "webpack serve --open chrome.exe"】
          • typescript
            • ts编译器
          • ts-loader
            • ts加载器,用于在webpack中编译ts文件,整合webpack和ts
          • html-webpack-plugin
            • webpack中html插件,用来自动创建html文件
          • clean-webpack-plugin
            • webpack中的清除插件,每次构建都会先清除目录,即帮你删了旧的build文件夹dist,方便新build入驻

# 备注:安装完之后,别忘了要在webpack.config.js里配置

  1. 根目录下创建webpack的配置文件webpack.config.js

    • const path = require("path");
      const HtmlWebpackPlugin = require("html-webpack-plugin");
      const { CleanWebpackPlugin } = require("clean-webpack-plugin");
      
      module.exports = {
          optimization:{
              minimize: false // 关闭代码压缩,可选
          },
      
          entry: "./src/index.ts",
          
          devtool: "inline-source-map",
          
          devServer: {
              contentBase: './dist'
          },
      
          output: {
              path: path.resolve(__dirname, "dist"),
              filename: "bundle.js",
              environment: {
                  arrowFunction: false // 关闭webpack的箭头函数,可选
              }
          },
      
          resolve: {
              extensions: [".ts", ".js"]
          },
          
          module: {
              rules: [
                  {
                      test: /\.ts$/,
                      use: {
                         loader: "ts-loader"     
                      },
                      exclude: /node_modules/
                  }
              ]
          },
      
          plugins: [
              new CleanWebpackPlugin(),
              new HtmlWebpackPlugin({
                  title:'TS测试'
              }),
          ]
      
      }
      

完整版webpack.config.js演示

  • // 引入一个包
    const path = require('path');
    // 引入html插件
    const HTMLWebpackPlugin = require('html-webpack-plugin');
    // 引入clean插件
    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    
    // webpack中的所有的配置信息都应该写在module.exports中
    module.exports = {
    
    // 指定入口文件
    entry: "./src/index.ts",
    
    // 指定打包文件所在目录
    output: {
        // 指定打包文件的目录
        path: path.resolve(__dirname, 'dist'),
        // 打包后文件的文件
        filename: "bundle.js",
    
        // 配置打包环境,告诉webpack不使用箭头
        environment:{
            arrowFunction: false
        }
    },
    
    // 指定webpack打包时要使用模块
    module: {
        // 指定要加载的规则
        rules: [
            {
                // test指定的是规则生效的文件
                test: /\.ts$/,
        // 要使用的loader,可同时指定多个loader,加载器是从下往上执行的
                use: [
                     // 配置babel
                     {
                         // 指定加载器
                         loader:"babel-loader",
                  // 设置babel,babel的作用:新语法转旧语法,想办法让
                  //老浏览器能够支持使用新类新对象新技术,提高代码兼容性
                         options: {
                             // 设置预定义的环境
                             presets:[
                                 [
                                     // 指定环境的插件
                                     "@babel/preset-env",
                                     // 配置信息
                                     {
                                         // 要兼容的目标浏览器
                                         targets:{
                                             "chrome":"58",
                                             "ie":"11"
                                         },
                                         // 指定corejs的版本
                                         "corejs":"3",
                             // 使用corejs的方式 "usage" 表示按需加载
                                         "useBuiltIns":"usage"
                                     }
                                 ]
                             ]
                         }
                     },
                    'ts-loader'//会先执行这个loader,因为加载器是从下
                //往上执行的,ts->js->旧js,以此兼容低版本和其它浏览器
                ],
                // 要排除的文件
                exclude: /node-modules/
            }
        ]
    },
    
    // 配置Webpack插件
    plugins: [
        new CleanWebpackPlugin(),
        new HTMLWebpackPlugin({
            // title: "这是一个自定义的title"
            template: "./src/index.html"
        }),
    ],
    
    // 用来设置引用模块,即为了告诉webpack哪些类型的文件可以作为模块被引用
    resolve: {
        extensions: ['.ts', '.js']//凡是以此结尾的文件均可作为模块被引用
    }
    
    };
    
  1. 根目录下创建tsconfig.json,配置可以根据自己需要

    • {
          "compilerOptions": {
              "target": "ES2015",
              "module": "ES2015",
              "strict": true
          }
      }
      
  2. 修改package.json添加如下配置

    • {
         "name": "part3",
         "version": "1.0.0",
         "description": "",
         "main": "index.js",
         "scripts": {
           "test": "echo \"Error: no test specified\" && exit 1",
           "build": "webpack",
           "start": "webpack serve --open chrome.exe"
         },
         "keywords": [],
         "author": "",
         "license": "ISC",
         "devDependencies": {
           "@babel/core": "^7.12.9",
           "@babel/preset-env": "^7.12.7",
           "babel-loader": "^8.2.2",
           "clean-webpack-plugin": "^3.0.0",
           "core-js": "^3.8.0",
           "html-webpack-plugin": "^4.5.0",
           "ts-loader": "^8.0.11",
           "typescript": "^4.1.2",
           "webpack": "^5.6.0",
           "webpack-cli": "^4.2.0",
           "webpack-dev-server": "^3.11.0"
         }
       }
      
      
  3. 在src下创建ts文件,并在并命令行执行npm run build对代码进行编译;或者执行npm start来启动开发服务器,即自动拉起页面,并自动监视,一旦代码更新则更新页面

5、Babel

  • 经过一系列的配置,使得TS和webpack已经结合到了一起,除了webpack,开发中还经常需要结合babel来对代码进行转换以使其可以兼容到更多的浏览器,在上述步骤的基础上,通过以下步骤再将babel引入到项目中。

    1. 安装依赖包:
      • npm i -D @babel/core @babel/preset-env babel-loader core-js
      • 共安装了4个包,分别是:
        • @babel/core
          • babel的核心工具
        • @babel/preset-env
          • babel的预定义环境,帮我们预先设置不同的浏览器环境
          • 比如,根据用户的一些浏览器的特性,在运行时动态的引入这些 polyfill (垫片),bundle size 最小。polyfill 是一个概念:垫片。在一个只支持 es5 的浏览器中,去运行 es6 。现在这些功能已经集成到@babel/preset-env里面去了
        • @babel-loader
          • babel在webpack中的加载器,使babel和loader结合的工具
        • core-js
          • core-js用来使老版本的浏览器支持新版ES语法,模拟js运行环境的代码,让老版本浏览器可以用新技术。比如你代码用了promise,就算你通过babel把js编译成老版本js,没有core-js,babel是处理不了promise的,promise代码还是无法在老版本浏览器中运行,因为以前就没有promise,所以需要引进core-js来协助。但是,如果是webpack自加的箭头函数,不会经过babel处理,所以会报错,所以要在webpack.config.js里面配置environment,上面里例子有。
备注:babel是webpack中处理浏览器兼容性问题的插件,babel的作用,说白了,就是,浏览器的版本,和语言本身有差异,所以我要对语言,进行编译降级。新语法转旧语法,想办法让老浏览器能够支持使用新类新对象新技术,提高代码兼容性**
  1. 修改webpack.config.js配置文件

    • ...略...
      module: {
          rules: [
              {
                  test: /\.ts$/,
                  use: [
                      {
                          loader: "babel-loader",
                          options:{
                              presets: [
                                  [
                                      "@babel/preset-env",
                                      {
                                          "targets":{
                                              "chrome": "58",
                                              "ie": "11"
                                          },
                                          "corejs":"3",
                                          "useBuiltIns": "usage"
                                      }
                                  ]
                              ]
                          }
                      },
                      {
                          loader: "ts-loader",
      
                      }
                  ],
                  exclude: /node_modules/
              }
          ]
      }
      ...略...
      
    • 如此一来,使用ts编译后的文件将会再次被babel处理,使得代码可以在大部分浏览器中直接使用,可以在配置选项的targets中指定要兼容的浏览器版本。

    • 补充一点,加入了webpack和typescript的项目,整个编译流程是这样的,你输入npm run build,webpack会找到ts-loader先把ts文件编译成js文件,再找babel把js转换成老版本js。