TypeScript学习笔记
TypeScript
准备
安装node.js
安装
npm install -g typescript
查看版本
tsc -v
在编写ts代码时,有时虽然没有写错,但是依然有报错信息 如:无法重新声明块范围变量,只需要执行tsc --init生成tsconfig.json文件即可解决
第一个TS程序
app.ts
var message:string = "Hello TypeScript!"
console.log(message)
TypeScript指令
使用tsc命令,将app.ts文件编译成为app.js文件

tsc app.ts
tsc指令可以同时编译多个ts文件
tsc app.ts app1.ts app2.ts
--declaration会再生成一个.d.ts文件
tsc app.ts --declaration
利用node命令,执行app.js文件
node app.js
Hello TypeScript!
自动热部署命令
首先生成tsconfig.json文件
tsc --init
tsconfig.json
{
"compilerOptions": {
"module": "ES2015",
"target": "ES2015",
"strict": true, // 严格模式
"outDir": "./dist", // 打包的位置
"noEmitOnError": true // 有错误语法将不进行编译
},
"include": [
"./src/**/*" // 编译哪里的ts文件
],
"exclude": [
"node_modules" // 忽略的文件
]
}
然后执行命令,就会自动热部署,不用每次修改代码都要手动部署
tsc -w
tsc 常用编译参数如下表所示:
| 序号 | 编译参数说明 |
|---|---|
| 1. | --help显示帮助信息 |
| 2. | --module载入扩展模块 |
| 3. | --target设置 ECMA 版本 |
| 4. | --declaration额外生成一个 .d.ts 扩展名的文件。tsc ts-hw.ts --declaration以上命令会生成 ts-hw.d.ts、ts-hw.js 两个文件。 |
| 5. | --removeComments删除文件的注释 |
| 6. | --out编译多个文件并合并到一个输出的文件 |
| 7. | --sourcemap生成一个 sourcemap (.map) 文件。sourcemap 是一个存储源代码与编译代码对应位置映射的信息文件。 |
| 8. | --module noImplicitAny在表达式和声明上有隐含的 any 类型时报错 |
| 9. | --watch在监视模式下运行编译器。会监视输出文件,在它们改变时重新编译。 |
TypeScript 保留关键字
TypeScript 保留关键字如下表所示:
| break | as | catch | switch |
|---|---|---|---|
| case | if | throw | else |
| var | number | string | get |
| module | type | instanceof | typeof |
| public | private | enum | export |
| finally | for | while | void |
| null | super | this | new |
| in | return | true | false |
| any | extends | static | let |
| package | implements | interface | function |
| do | try | yield | const |
| continue |
空白和换行
TypeScript 会忽略程序中出现的空格、制表符和换行符。
空格、制表符通常用来缩进代码,使代码易于阅读和理解。
TypeScript 区分大小写
TypeScript 区分大写和小写字符。
分号是可选的
每行指令都是一段语句,你可以使用分号或不使用, 分号在 TypeScript 中是可选的,建议使用。
以下代码都是合法的:
console.log("Runoob")
console.log("Google");
如果语句写在同一行则一定需要使用分号来分隔,否则会报错,如:
console.log("Runoob");console.log("Google");
TypeScript 注释
和JavaScript一样
// 单行注释
/*
多行注释
多行注释
多行注释
*/
TypeScript面向对象
TypeScript 面向对象编程实例:
class Site {
name():void {
console.log("Runoob")
}
}
var obj = new Site();
obj.name();
以上实例定义了一个类 Site,该类有一个方法 name(),该方法在终端上输出字符串 Runoob。
new 关键字创建类的对象,该对象调用方法 name()。
编译后生成的 JavaScript 代码如下:
var Site = /** @class */ (function () {
function Site() {
}
Site.prototype.name = function () {
console.log("Runoob");
};
return Site;
}());
var obj = new Site();
obj.name();
执行以上 JavaScript 代码,输出结果如下:
Runoob
定义类
// 定义一个Person类
class Person {
// 实例属性,必须实例化访问
myName:string = "ganto"
// 静态属性,可以直接访问
static myAge:number = 19
// 定义只读的属性
readonly mySex:string = "男"
// 定义静态只读的属性
static readonly mySchool:string = "B站"
// 实例方法
sayMe():void{
console.log(this.myName, Person.myAge)
}
// 静态方法
static lookMe():void{
console.log(this.myAge)
}
}
const obj = new Person() // 实例化对象
console.log(obj.myName) // 访问实例属性
obj.sayMe(); // 访问实例方法
console.log(Person.myAge) // 访问静态属性
Person.lookMe() // 访问静态方法
构造方法
class Person {
myName:string
myAge:number
constructor(myName:string, myAge:number) {
console.log(this)
this.myName = myName
this.myAge = myAge
}
bark():void{
console.log(this.myName, ",",this.myAge,"岁了,汪汪汪!")
}
}
const person = new Person("旺财", 2)
person.bark()
const person1 = new Person("小明", 5)
person1.bark()
封装
封装的好处就是,类中的属性定义成私有的,通过特有的getter、setter方法进行暴露和操作,可以在setter方法中控制属性
class Person {
private name:string
private age:number
constructor(name:string, age:number) {
this.name = name
this.age = age
}
getName() {
return this.name
}
setName(name:string) {
if(name.length == 2 || name.length == 3) {
this.name = name
}else{
console.log("name不合法")
}
}
getAge() {
return this.age
}
setAge(age:number) {
if(age > 0 && age < 150) {
this.age = age
}else{
console.log("age不合法")
}
}
}
const person = new Person("猪八戒",19)
console.log(person)
// person.name = "孙悟空" // 属性“name”为私有属性,只能在类“Person”中访问。
// person.age = -18 // 属性“age”为私有属性,只能在类“Person”中访问。
person.setName("孙悟空1")
person.setAge(-18)
console.log(person.getName(), person.getAge())
继承
class Person {
myName:string
myAge:number
constructor(myName:string, myAge:number) {
this.myName = myName
this.myAge = myAge
}
say():void{
console.log(this.myName, "",this.myAge,)
}
}
class XiaoHua extends Person {
}
class XiaoMing extends Person {
mySex:string
constructor(myName:string,myAge:number,mySex:string) {
super(myName,myAge)
this.mySex = mySex
}
say(): void {
console.log(this.myName, "", this.myAge, "", this.mySex)
}
}
const xiaohua = new XiaoHua("小花", 18)
xiaohua.say()
const xiaoming = new XiaoMing("小明", 20, "女")
xiaoming.say()
super(myName, myAge)意思就是将值传递给父类,super.say()就是调用父类中的say方法
抽象类
抽象类只能被子类继承,不可以实例化
// 抽象类
abstract class Animal {
name:string
constructor(name:string){
this.name = name
}
sayHello():void {
console.log("动物在叫")
}
}
class Dog extends Animal{
sayHello(): void {
console.log("汪汪汪汪")
}
}
const dog = new Dog("旺财")
dog.sayHello()
// const she = new Animal("蛇") // 抽象类只能被继承,不可以实例化
抽象类中可以有普通方法,也可以有抽象方法,抽象方法必须被子类实现
// 抽象类
abstract class Animal {
name:string
constructor(name:string){
this.name = name
}
sayHello():void {
console.log("动物在叫")
}
// 抽象方法
abstract look():void
}
class Dog extends Animal{
sayHello(): void {
console.log("汪汪汪汪")
}
look(): void {
console.log("狗子在看")
}
}
const dog = new Dog("旺财")
dog.sayHello()
dog.look()
// const she = new Animal("蛇") // 抽象类只能被继承,不可以实例化
接口
接口可以当成类型声明去使用,类型声明不可重复,但接口可以重复,重复的接口会进行合并
// 描述一个对象的类型
type myType = {
name: string,
age: number,
[propname:string]:any
}
const obj: myType = {
name: 'sss',
age: 11
}
// 接口和上方 定义一个对象的类型 很像,接口用来 定义一个类的结构(用来包含哪些属性和方法)
interface myInterface {
name: string,
age: number
}
interface myInterface {
gender: string
}
const obj1: myInterface = {
name: 'sss',
age: 11,
gender: "男",
}
接口中定义的属性,不能有实际值
interface myInterface {
name: string,
age: number
}
interface myInterface {
gender: string
}
const obj1: myInterface = {
name: 'sss',
age: 11,
gender: "男",
}
接口中只能有抽象方法,不能有普通方法,在接口中定义抽象方法一般省略abstract关键字,没有方法体
interface myInterface {
sayHello():void
}
const obj1: myInterface = {
sayHello() {
}
}
类实现接口
interface myInterface {
name: string,
sayHello():void
}
class MyClass implements myInterface {
name:string
constructor(name:string) {
this.name = name
}
sayHello():void{
console.log("sayHello")
}
}
接口可以继承接口
interface baseInter {
sex:string
}
interface myInterface extends baseInter {
name: string,
sayHello():void
}
class MyClass implements myInterface {
name:string
sex:string
constructor(name:string, sex:string) {
this.name = name
this.sex = sex
}
sayHello():void{
console.log("sayHello")
}
}
TypeScript 基础类型
基础类型
| 数据类型 | 关键字 | 描述 |
|---|---|---|
| 任意类型 | any | 声明为any的变量可以赋予任意类型的值。 |
| 字符串 | string | 一个字符系列,使用单引号(')或双引号(")来表示字符串类型。反引号(`)来定义多行文本和内嵌表达式。 |
| 数字 | number | 双精度 64 位浮点值。它可以用来表示整数和分数。 |
| 布尔 | boolean | 表示逻辑值:true 和 false。 |
| 数组 | 声明变量为数组。 | |
| 元组Tuple | 元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。 | |
| 枚举 | enum | 枚举类型用于定义数值集合。 |
| void | void | 用于标识方法返回值的类型,表示该方法没有返回值。 |
| null | null | 表示对象值缺失。 |
| undefined | undefined | 用于初始化变量为一个未定义的值 |
| never | never | never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。 |
注意:TypeScript 和 JavaScript 没有整数类型。
注意:在js中 如果声明变量同时赋值,变量的类型会直接被设置为所赋值的类型。如下代码是报错的。
/* this is a typescript code */
let a = 1 // ts中声明变量直接赋值会直接锁定变量类型
a = '2' // 这里会报错
Any
任意值是 TypeScript 针对编程时类型不明确的变量使用的一种数据类型,它常用于以下三种情况。
1、变量的值会动态改变时,比如来自用户的输入,任意值类型可以让这些变量跳过编译阶段的类型检查,示例代码如下:
let x: any = 1; // 数字类型
x = 'I am who I am'; // 字符串类型
x = false; // 布尔类型
改写现有代码时,任意值允许在编译时可选择地包含或移除类型检查,示例代码如下:
let x: any = 4;
x.ifItExists(); // 正确,ifItExists方法在运行时可能存在,但这里并不会检查
x.toFixed(); // 正确
定义存储各种类型数据的数组时,示例代码如下:
let arrayList: any[] = [1, false, 'fine'];
arrayList[1] = 100;
console.log(arrayList)
任意类型的数组,有点像元组,但是元组声明了固定类型后就无法修改类型,任意类型的数组要比元组更加随意。
unknown
下方的代码都不会报错,那么这么看unknown和any一样?但其实有区别
let e: unknown
e = 10
e = 'hello'
e = true
unknown和any的区别:
let a:any
a = true
let b: string
b = 'hello'
b = a // 这时b的类型将会被设置为boolean
let a: unknown
a = 'string'
let b: string
b = 'hello'
b = a // 这里将会报错,因为a的类型为unknown,无法赋值给其他类型
- any类型可以给任意类型赋值,赋值过后的变量也会被改为any所属的类型 (真就any霍霍所有类型)
- unknown类型不可以给其他类型赋值
- unknown和any之间可以相互赋值
- 其他类型可以给unknown和any赋值
字符串
let myName: string = 'xiaoming'
数字
let age: number = 12
let binaryLiteral: number = 0b1010; // 二进制
let octalLiteral: number = 0o744; // 八进制
let decLiteral: number = 6; // 十进制
let hexLiteral: number = 0xf00d; // 十六进制
字面量赋值
赋值过后无法修改,类似于常量
let a: 10
a = 10
a = 11 // 这里的代码会报错,字面量a为10常量,a将无法修改为11
联合类型
let b: "male" | "female"
b = "male"
b = "female"
b = "hello" // 这里的代码会报错,因为b只能为male或者female
let c: boolean | string
c = true
c = 'hello'
c = 1 // 这里的代码会报错,因为c的类型只能为boolean或者string
布尔
let flag: boolean = true
let unflag: boolean = false
数组
// 在元素类型后面加上[]
let arr: number[] = [1, 2];
// 或者使用数组泛型
let arr: Array<number> = [1, 2];
元组
固定长度,单独固定每项类型的数组
let x: [string, number];
x = ['Runoob', 1]; // 运行正常
x = [1, 'Runoob']; // 报错
console.log(x[0]); // 输出 Runoob
枚举
enum Color {Red, Green, Blue};
let c: Color = Color.Blue;
console.log(c); // 输出 2
enum Gender{
Male,
Female
}
let i: {name: string, gender: Gender}
i = {
name: '孙悟空',
gender: Gender.Male
}
console.log(i.gender === 1) // true
console.log(i.gender === Gender.Male) // true
void
void 用来表示空,以函数为例,就表示没有返回值的函数
function hello(): void {
alert("Hello Runoob");
}
never 类型
never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。这意味着声明为 never 类型的变量只能被 never 类型所赋值,在函数中它通常表现为抛出异常或无法执行到终止点(例如无限循环),示例代码如下:
let x: never;
let y: number;
// 编译错误,数字类型不能转为 never 类型
x = 123;
// 运行正确,never 类型可以赋值给 never类型
x = (()=>{ throw new Error('exception')})();
// 运行正确,never 类型可以赋值给 数字类型
y = (()=>{ throw new Error('exception')})();
// 返回值为 never 的函数可以是抛出异常的情况
function error(message: string): never {
throw new Error(message);
}
// 返回值为 never 的函数可以是无法被执行到的终止点的情况
function loop(): never {
while (true) {}
}
类型断言
类型断言有两种形式。 其一是“尖括号”语法:
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
另一个为as语法:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
两种形式是等价的。 至于使用哪个大多数情况下是凭个人喜好;然而,当你在TypeScript里使用JSX时,只有as语法断言是被允许的。
例子:
let a:unknown
a = 'hello'
let b: string
b = 'b'
b = a as string // 这里使用类型断言后,不再报错
console.log(b, typeof b);
如下代码,使用类型断言可以欺骗编译器,可以将b的类型修改成为number
let a:unknown
a = 1
let b: string
b = 'b'
b = a as string
console.log(b, typeof b); // 1 number
对象Object
let a: object;
a = {}
a = function() {}
let b: {name: string};
b = {name: '喜喜'}
因为在js中很多东西都是对象,直接声明let a: object,会有一些不好。可以用第二种方式声明,明确那种object
如果一个对象必须转递一个name属性,其他属性可以随意增删,属性类型也任意,可以如下例子:
let a: {name: string, [propName: string]: any}
a = {name: '哈哈', age: 10, sex: '女'}
其中[propName: string]: any中的前边部分[propName: string]表示属性名为字符串,any表示属性的值为任意值。
let d: (a: number, b: number) => number;
d = function(n1, n2): number{
return n1+n2
}
与& 或|
let j: {name: string} & {age: number}
let x: string | number
let k: 1 | 2 | 3 | 4 | 5
类型别名
type myType = string;
let a: myType;
type myType1 = 1 | 2 | 3 | 4 | 5;
let b: myType1
type Cls = {
a: boolean,
b: boolean
}
const cls: Cls = {
a: true,
b: true
}
TypeScript条件语句
- if 语句 - 只有当指定条件为 true 时,使用该语句来执行代码
- if...else 语句 - 当条件为 true 时执行代码,当条件为 false 时执行其他代码
- if...else if....else 语句- 使用该语句来选择多个代码块之一来执行
- switch 语句 - 使用该语句来选择多个代码块之一来执行
if
if...else
if...else if...else
switch…case
TypeScript循环
for
for...in
for...of
forEach
every
while
do...while
TypeScript函数
函数返回值
function function_name():return_type {
// 语句
return value;
}
- return_type 是返回值的类型。
- return 关键词后跟着要返回的结果。
- 一般情况下,一个函数只有一个 return 语句。
- 返回值的类型需要与函数定义的返回类型(return_type)一致。
案例
// 函数定义
function greet():string { // 返回一个字符串
return "Hello World"
}
function caller() {
var msg = greet() // 调用 greet() 函数
console.log(msg)
}
// 调用函数
caller()
- 实例中定义了函数 greet(),返回值的类型为 string。
- greet() 函数通过 return 语句返回给调用它的地方,即变量 msg,之后输出该返回值。
带参数函数
在调用函数时,您可以向其传递值,这些值被称为参数。
这些参数可以在函数中使用。
您可以向函数发送多个参数,每个参数使用逗号 , 分隔:
语法格式如下所示:
function func_name( param1 [:datatype], param2 [:datatype]) {
}
- param1、param2 为参数名。
- datatype 为参数类型。
实例
function add(x: number, y: number): number {
return x + y;
}
console.log(add(1,2))
- 实例中定义了函数 add(),返回值的类型为 number。
- add() 函数中定义了两个 number 类型的参数,函数内将两个参数相加并返回。
可选参数和默认参数
可选参数
在 TypeScript 函数里,如果我们定义了参数,则我们必须传入这些参数,除非将这些参数设置为可选,可选参数使用问号标识 ?。
实例
function buildName(firstName: string, lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // 错误,缺少参数
let result2 = buildName("Bob", "Adams", "Sr."); // 错误,参数太多了
let result3 = buildName("Bob", "Adams"); // 正确
以下实例,我们将 lastName 设置为可选参数:
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // 正确
let result2 = buildName("Bob", "Adams", "Sr."); // 错误,参数太多了
let result3 = buildName("Bob", "Adams"); // 正确
可选参数必须跟在必需参数后面。 如果上例我们想让 firstName 是可选的,lastName 必选,那么就要调整它们的位置,把 firstName 放在后面。
如果都是可选参数就没关系。
默认参数
我们也可以设置参数的默认值,这样在调用函数的时候,如果不传入该参数的值,则使用默认参数,语法格式为:
function function_name(param1[:type],param2[:type] = default_value) {
}
注意:参数不能同时设置为可选和默认。
实例
以下实例函数的参数 rate 设置了默认值为 0.50,调用该函数时如果未传入参数则使用该默认值:
function calculate_discount(price:number,rate:number = 0.50) {
var discount = price * rate;
console.log("计算结果: ",discount);
}
calculate_discount(1000) // 500
calculate_discount(1000,0.30) // 300
输出结果为:
计算结果: 500
计算结果: 300
剩余参数
有一种情况,我们不知道要向函数传入多少个参数,这时候我们就可以使用剩余参数来定义。
剩余参数语法允许我们将一个不确定数量的参数作为一个数组传入。
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
函数的最后一个命名参数 restOfName 以 ... 为前缀,它将成为一个由剩余参数组成的数组,索引值从0(包括)到 restOfName.length(不包括)。
function addNumbers(...nums:number[]) {
var i;
var sum:number = 0;
for(i = 0;i<nums.length;i++) {
sum = sum + nums[i];
}
console.log("和为:",sum)
}
addNumbers(1,2,3)
addNumbers(10,10,10,10,10)
输出结果为:
和为: 6
和为: 50
new Map()
let nameSiteMapping = new Map();
// 设置 Map 对象
nameSiteMapping.set("Google", 1);
nameSiteMapping.set("Runoob", 2);
nameSiteMapping.set("Taobao", 3);
// 获取键对应的值
console.log(nameSiteMapping.get("Runoob")); // 2
// 判断 Map 中是否包含键对应的值
console.log(nameSiteMapping.has("Taobao")); // true
console.log(nameSiteMapping.has("Zhihu")); // false
// 返回 Map 对象键/值对的数量
console.log(nameSiteMapping.size); // 3
// 删除 Runoob
console.log(nameSiteMapping.delete("Runoob")); // true
console.log(nameSiteMapping);
// 移除 Map 对象的所有键/值对
nameSiteMapping.clear(); // 清除 Map
console.log(nameSiteMapping);
/**
* new Map() // 实例化对象
* set() // 设置键值对
* get() // 获取键对应的值
* has() // 获取是否有键
* delete() // 删除该键值对
* clear() // 清空
* size // 返回长度
*/
接口
例子
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
利用接口重写
interface LabelledValue {
label: string;
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
哈哈,暂时没get到接口这样写的意义
可选属性
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): {color: string; area: number} {
let newSquare = {color: "white", area: 100};
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({});
console.log(mySquare)
带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。
接口里的属性添加了?符号后,在调用函数的时候,就可以不用传递该属性。如上方代码,是完全正确的。
只读属性
一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly来指定只读属性:
interface Point {
readonly x: number;
readonly y: number;
}
你可以通过赋值一个对象字面量来构造一个Point。 赋值后, x和y再也不能被改变了。
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!
TypeScript具有ReadonlyArray<T>类型,它与Array<T>相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改:
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
上面代码的最后一行,可以看到就算把整个ReadonlyArray赋值到一个普通数组也是不可以的。 但是你可以用类型断言重写:
a = ro as number[];
readonly vs const
最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用const,若做为属性则使用readonly。
const一般用在变量声明:const num = 1
readonly一般用在属性中:{ readonly num1: number }
JSX
非官网教程,写的还可以:https://www.w3cschool.cn/typescript/typescript-jsx.html
三斜线指令
/// <reference path="react.d.ts" />
编译选项tsconfig.json
通过以下命令监听app.ts的变化,一旦变化则会重新编译。-w 即 watch
tsc app.ts -w
通过tsc --init生成tsconfig.json文件,然后执行tsc命令会将项目中的ts文件全部编译成js文件,必须要生成tsconfig.json文件,只有tsconfig.json文件存在后,tsc和tsc -w命令才可以生效。
tsc --init
tsc # 编译全部ts文件
tsc -w # 监听全部ts文件的变化
tsconfig.json
-
include: 用来指定哪些ts文件需要被编译
- ** 表示任意目录
- * 表示任意文件
{ "include": [ "./src/**/*" /* src目录下的任意目录下的任意文件 */ ] } -
exclude: 用来指定不需要被编译的路径
- 默认值:["node_modules", "bower_components", "jspm_packages"]
{ "exclude": [ "./src/hello/**/*" ] } -
extends: 定义被继承的配置文件
{ "extends": "./configs/base" } -
files: 指定被编译文件的列表,只有需要编译的文件少时才会用到
{ "files": [ "core.ts", "sys.ts", ... ] } -
compilerOptions: 编译选项是配置文件中非常重要也比较复杂的配置选项
-
target: 用来指定ts被编译成的ES版本
{ "compilerOptions": { "target": "ES6" } } -
module: 指定要使用的模块化的规范
{ "compilerOptions": { "module": "ES6" } } -
lib: 用来指定项目中要使用的库
{ "compilerOptions": { "lib": [ "dom" ] } }如上,dom为需要使用获取dom的包,如document.getElementById('box')
-
outDir: 指定编译后文件所在的目录
{ "compilerOptions": { "outDir": "./dist" } } -
outFile: 将多个ts编译成的js文件合并成一个文件
{ "compilerOptions": { "outFile": "./dist/app.js" } }设置outFile后,所有的全局作用域中的代码会合并到同一个文件中
-
allowJs: 是否对js文件进行编译,默认是false
{ "compilerOptions": { "allowJs": true } } -
checkJs: 是否检查js代码是否符合语法规范,默认是false
{ "compilerOptions": { "checkJs": true } } -
removeComments: 是否移除注释,默认是false
{ "compilerOptions": { "removeComments": true } } -
noEmit: 不生成编译后的js文件,默认值false
{ "compilerOptions": { "noEmit": true } } -
noEmitOnError: 当有错误时,不生成就编译后的js文件,默认值false
{ "compilerOptions": { "noEmitOnError": true } }
语法检查相关的配置
-
alwaysStrict: 开始严格模式,默认值false
{ "compilerOptions": { "alwaysStrict": true } } -
noImplicitAny: 不允许隐式any类型,默认值false
{ "compilerOptions": { "noImplicitAny": true } } -
noImplicitThis: 不允许不明确的类型的this,默认值false
{ "compilerOptions": { "noImplicitThis": true } } -
strictNullChecks: 严格的检查空值,默认值false
{ "compilerOptions": { "strictNullChecks": true } } -
strict: 所有严格模式的总开关,默认值false
{ "compilerOptions": { "strict": true } }如果没有单独设置其他的属性,在设置strict后,会将其他语法检查配置也和strict一直,除非单独设置其他配置
-
使用Webpack打包Ts代码
初始化package.json
通过npm初始化,生成package.json文件,管理项目
npm init -y
安装依赖
npm i -D webpack webpack-cli typescript ts-loader
项目根目录创建webpack.config.js配置文件,跟package.json同级
webpack.config.js
// 引入一个包
const path = require('path')
// webpack中的所有的配置信息都应该写在module.exports中
module.exports = {
// 指定入口文件
entry: './src/index.ts',
// 指定打包文件所在的目录
output: {
// 指定打包文件的目录
path: path.resolve(__dirname, 'dist'),
// 打包后文件的名字
filename: 'bundle.js'
},
// 指定webpack打包时要使用的模块
module: {
// 指定要加载的规则
rules: [
{
// test指定的是规则生效的文件
test: /\.ts$/,
// 要使用的loader
use: 'ts-loader',
// 要排除的文件
exclude: /node-modules/
}
]
}
}
tsconfig.json
使用指令创建tsconfig.json文件
tsc --init
/* tsconfig.json */
{
"compilerOptions": {
"module": "ES2015",
"target": "ES2015",
"strict": true
}
}
package.json
在package.json中添加这样的配置
/* package.json */
{
"scripts": {
"build": "webpack"
}
}
npm run build
上面配置完成后,通过npm run build进行打包
安装html-webpack-plugin
该插件在执行npm run build打包时,会在/dist/文件夹下创建index.html文件,并且可以自动引入js文件和配置一些内容。
npm i -D html-webpack-plugin
/* webpack.config.js */
// 在webpack中引入插件
const HTMLWebpackPlugin = require('html-webpack-plugin')
// 配置Webpack插件
plugins: [
new HTMLWebpackPlugin({
// index.html中的title
title: '自定义title',
// 会将src下的index.html打包到dist文件夹下
template: './src/index.html'
}),
]
Webpack开发服务器webpack-dev-server
webpack-dev-server支持热重载
安装
cnpm i -D webpack-dev-server
cnpm i -D clean-webpack-plugin
clean-webpack-plugin 是在打包前将旧的打包生成的文件先删除。
/* webpack.config.js */
// 在webpack中引入插件
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
// 配置Webpack插件
plugins: [
new HTMLWebpackPlugin({
// index.html中的title
title: '自定义title',
// 会将src下的index.html打包到dist文件夹下
template: './src/index.html'
}),
new CleanWebpackPlugin(),
]
在package.json中进行配置
/* package.json */
{
"scripts": {
"build": "webpack",
"start": "webpack serve --open chrome.exe"
}
}
配置webpack.config.js使其支持ts和js扩展名模块的导入
{
resolve: {
extensions: ['.ts', '.js']
}
}
安装babel
cnpm i -D @babel/core @babel/preset-env babel-loader core-js
webpack.config.js
// 引入一个包
const path = require('path')
// 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
use: [
// 配置babel
{
// 指定加载器
loader: "babel-loader",
// 设置babel
options: {
// 设置预定义的环境
presets:[
[
// 指定环境的插件
"@babel/preset-env",
{
// 要兼容的目标浏览器
targets: {
"chrome":"58",
"ie":"11"
},
// 指定corejs的版本
"corejs": "3",
// 使用corejs的方法 "usage":表示按需加载
"useBuiltIns": "usage"
}
]
]
}
},
'ts-loader'
],
// 要排除的文件
exclude: /node-modules/
}
]
}
}
面向对象
类
语法
class 类名{
属性名: 类型;
constructor(参数: 类型) {
this.属性名 = 参数;
}
方法名() {
...
}
}
实例
class Person{
// 实例属性 通过实例化对象访问
name: String;
readonly age: number;
// 静态属性 可以直接通过类名访问
static sex: String = "男";
// 构造器
constructor(name: String, age: number) {
this.name = name;
this.age = age;
}
// 定义奔跑方法
run (): String {
console.log(this.name, this.age);
return "奔跑中";
}
}
const per = new Person("张三", 18);
// per.age = 20; // 只读属性 无法修改
console.log(per.run(), Person.sex);
继承
继承后可以将父类中的方法和属性完全继承过来,也可以重写父类中的方法
class Animal{
name: String;
age: number;
constructor(name: String, age: number) {
this.name = name;
this.age = age;
}
run(): void {
console.log("动物在运动");
}
}
class Dog extends Animal{
run(): void {
console.log(this.name + "" + this.age + "岁,在运动");
}
}
const dog = new Dog("小黑", 5);
dog.run();
如果需要在子类中添加新的属性,然后需要在子类中写构造器的话,需要手动调用父类中的构造器。
class Animal{
name: String;
age: number;
constructor(name: String, age: number) {
this.name = name;
this.age = age;
}
run(): void {
console.log("动物在运动");
}
}
class Dog extends Animal{
sex: String;
constructor(name: String, age: number, sex: String) {
super(name, age);
this.sex = sex;
}
run(): void {
console.log(this.name + "" + this.age + "岁," + this.sex + ",在运动");
}
}
const dog = new Dog("小黑", 5, '公');
dog.run();
子类中的构造器需要也把父类中的属性也传入,因为子类的构造器相当于一个中转,将参数中传给父类中的构造器。
抽象类
通过abstract关键字定义的类,就是抽象类,抽象类不能实例化对象,只能被子类继承。
抽象类中存在抽象方法,抽象方法也是通过abstract关键字定义的,并且抽象方法只能定义在抽象类中,子类必须对父类中的抽象方法进行重写。抽象方法没有方法体。,基本上和java中的抽象方法保持一致。
abstract class Animal{
name: String;
constructor(name: String) {
this.name = name;
}
abstract run(): void;
}
class Dog extends Animal{
age: number;
constructor(name: String, age: number) {
super(name);
this.age = age;
}
run(): void {
console.log(this.name + "" + this.age + "岁,在运动");
}
}
const dog = new Dog("小黑", 5);
dog.run();
接口
例子
type myType = {
name: String,
age: number
}
const obj: myType = {
name: 'sss',
age: 10
}
使用接口实现
interface myInterface {
name: String;
age: number;
}
const obj: myInterface = {
name: 'sss',
age: 10
}
接口是用来定义一个类的结构,用来定义一个类中应该包含哪些属性和方法,同时接口也可以当成类型声明去使用。
多接口同名合并
type不允许定义同名的类型声明,而接口可以定义多个同名的接口,只是定义多个相同的接口,会将多个同名接口融合一起。
interface myInterface {
name: String;
age: number;
}
interface myInterface {
sex: String;
}
const obj: myInterface = {
name: "小户",
age: 10,
sex: "男"
}
接口中的属性和方法
接口中的属性和方法都是只定义,方法只能为抽象方法,属性只定义不赋值。
interface myInterface {
name: String;
sayHello(): void;
}
实现接口
类实现接口,需要将接口中的抽象方法全部重写,然后将接口中的参数利用构造方法进行赋值操作。
interface myInterface {
name: String;
sayHello(): void;
}
class MyClass implements myInterface {
name: String;
constructor(name: string) {
this.name = name;
}
sayHello(): void {
console.log("注意看,这个男人他叫" + this.name)
}
}
const myClass = new MyClass("小帅");
myClass.sayHello();
属性的封装
public 公共
private 私有
protected 受保护
TS中的属性和方法关键字和Java中有所不同,TS中没有省略的,只有public、private、protected,这三个修饰符的作用和java中的一模一样。
如果省略则表示public公共的。
私有变量依然可以定义getter、setter方法。
;(function(){
// 定义一个表示人的类
class Person{
private name: String;
private age: number;
constructor(name: String, age: number) {
this.name = name;
this.age = age;
}
public setName(name: String): void {
this.name = name;
}
public getName(): String {
return this.name;
}
public setAge(age: number): void {
this.age = age;
}
public getAge(): number {
return this.age;
}
}
const per = new Person("孙悟空", 18);
per.setName("猪八戒");
per.setAge(-100);
console.log(per.getName(), per.getAge());
})();
getter、setter方法的好处就是,让本类中的变量不会被随意修改,可以在setter方法中进行控制。
;(function(){
// 定义一个表示人的类
class Person{
private name: String;
private age: number;
constructor(name: String, age: number) {
this.name = name;
this.age = age;
}
public setName(name: String): void {
this.name = name;
}
public getName(): String {
return this.name;
}
public setAge(age: number): void {
if(age >= 0) {
this.age = age;
}
}
public getAge(): number {
return this.age;
}
}
const per = new Person("孙悟空", 18);
per.setName("猪八戒");
per.setAge(-100);
console.log(per.getName(), per.getAge());
})();
TtypScript中的get、set方法的另一种写法
// 定义一个表示人的类
class Person{
private _name: String;
private _age: number;
constructor(name: String, age: number) {
this._name = name;
this._age = age;
}
get name() {
return this._name;
}
set name(name: String) {
this._name = name;
}
get age() {
return this._age;
}
set age(age: number) {
if(age >= 0) {
this._age = age;
}
}
}
const per = new Person("孙悟空", 18);
per.name = "猪八戒";
per.age = -100;
console.log(per.name, per.age);
构造器和属性结合
以下两种写法完全一样。
// 定义一个表示人的类
class Person{
public name: String;
constructor(name: String) {
this.name = name;
}
}
const per = new Person("红孩儿");
console.log(per.name);
// 定义一个表示人的类
class Person{
constructor(public name: String) {}
}
const per = new Person("红孩儿");
console.log(per.name);
泛型
在定义函数或者类时,如果遇到类型不明确就可以使用泛型。
// function fn(num: ?): ? {
// return num;
// }
function fn<T>(num: T): T{
return num;
}
console.log(fn("你好,泛型。")); // 不指定泛型类型,TS可以自动对类型进行推断
console.log(fn<String>("你好,泛型。")); // 指定泛型类型
console.log(fn<number>(24)); // 指定泛型类型
泛型可以指定多个
function fn<T, K>(num: T, num1: K): T{
console.log(num1);
return num;
}
console.log(fn('123', 123));
console.log(fn<String, Number>('123', 123));
限制泛型的类型
interface Inter{
length: number;
}
function fn<T extends Inter>(a: T): number{
return a.length;
}
fn("123"); // 字符串有.length属性
fn(123); // 报错,因为数字没有.length属性
fn({length: 1}); // 自定义对象内有.length属性,所以不会报错
以上泛型T是实现了接口Inter,但是这里使用的是extends关键字,而不是implements,需要特别注意。
类中的泛型
class MyClass<T> {
name: T;
constructor(name: T) {
this.name = name;
}
}
const mc = new MyClass<String>("孙悟空");
const mc1 = new MyClass<Number>(123);
console.log(mc,mc1);

浙公网安备 33010602011771号