第一章 快速入门
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 开发环境搭建
-
下载Node.js
-
安装Node.js
-
使用npm全局安装typescript编译器
- 进入命令行
- 输入:npm i -g typescript
-
创建一个ts文件
-
使用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 -
类型:
类型 例子 描述 number 1, -33, 2.5 任意数字 string 'hi', "hi", hi任意字符串 boolean true、false 布尔值true或false 字面量 其本身 限制变量的值就是该字面量的值 any * 任意类型 unknown * 类型安全的any void 空值(undefined) 没有值(或undefined) never 没有值 不能是任何值 object {name:'孙悟空'} 任意的JS对象 array [1,2,3] 任意JS数组 tuple [4,5] 元组,TS新增类型,固定长度数组 enum enum{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
- 严格检查属性是否初始化
- strict
-
额外检查
- noFallthroughCasesInSwitch
- 检查switch语句包含正确的break
- noImplicitReturns
- 检查函数没有隐式的返回值
- noUnusedLocals
- 检查未使用的局部变量
- noUnusedParameters
- 检查未使用的参数
- noFallthroughCasesInSwitch
-
高级
- allowUnreachableCode
- 检查不可达代码
- 可选值:
- true,忽略不可达代码
- false,不可达代码将引起错误
- noEmitOnError
- 有错误的情况下不进行编译
- 默认值:false
- allowUnreachableCode
-
-
-
-
-
-
示例汇总
{
/*
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。
-
步骤:
-
初始化项目
- 进入项目根目录,执行命令
npm init -y- 主要作用:创建package.json文件
- 进入项目根目录,执行命令
-
下载构建工具
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
- 共安装了7个包
-
# 备注:安装完之后,别忘了要在webpack.config.js里配置
-
根目录下创建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']//凡是以此结尾的文件均可作为模块被引用 } };
-
根目录下创建tsconfig.json,配置可以根据自己需要
-
{ "compilerOptions": { "target": "ES2015", "module": "ES2015", "strict": true } }
-
-
修改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" } }
-
-
在src下创建ts文件,并在并命令行执行
npm run build对代码进行编译;或者执行npm start来启动开发服务器,即自动拉起页面,并自动监视,一旦代码更新则更新页面
5、Babel
-
经过一系列的配置,使得TS和webpack已经结合到了一起,除了webpack,开发中还经常需要结合babel来对代码进行转换以使其可以兼容到更多的浏览器,在上述步骤的基础上,通过以下步骤再将babel引入到项目中。
- 安装依赖包:
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/core
- 安装依赖包:
备注:babel是webpack中处理浏览器兼容性问题的插件,babel的作用,说白了,就是,浏览器的版本,和语言本身有差异,所以我要对语言,进行编译降级。新语法转旧语法,想办法让老浏览器能够支持使用新类新对象新技术,提高代码兼容性**
-
修改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。
-