一、TS开发环境搭建
1、安装编译TS的工具包
问题:为什么要安装编译TS的工具包?
答:Node.js/浏览器,只认识JS代码,不认识TS 代码。需要先将TS 代码转化为J5 代码,然后才能运行。
安装命令:
#安装命令
npm i -g typescript
#检查是否安装成功(查看typescript的版本)
tsc -v
2、编写,编译,并运行TS代码
编写:在vscode中创建一个hello.ts的文件:内容如下:
console.log("你好TS");
const age: number = 18;
console.log(age);
编译并运行:
第一种:在vscode终端中执行如下代码(会在同级目录下生成一个同名JS文件):
#将hello.ts这个TS文件编译成一个JS文件(编译成功后会在这个TS同级目录中生成一个同名的hello.js文件)
tsc hello.ts
#执行js文件
node hello.js //会在页面输出:你好TS 和 18
第二种:在vscode终端中执行如下代码(不会在同级目录下生成一个同名JS文件):
因为第一种每次修改代码后,都要重复执行两个命令,才能运行TS代码,太繁琐
简化方式:使用ts-node包,直接在 Node.js 中执行TS 代码。
安装命令:
npm i -g ts-node (ts-node包提供了ts-node 命令)
编译并运行:
ts-node hello.ts //解释:ts-node 命令在内部偷偷的将TS->JS,然后,再运行JS代码。会在页面输出:你好TS 和 18 (不会生成一个同名的JS文件了)
二、常用基础类型
可以将 TS 中的常用基础类型分为两类
1.JavaScript 已有类型
- 原始类型: number /string /boolean /null /undefined /symbol
- 对象类型:object(数组、对象、函数等)
2. TypeScript 新增类型
- 联合类型、自定义类型(类型别名)、接口、元祖、字面量类型、枚举、void、any 等
三、TS中的类型声明
1、原始类型的声明
//数值类型
let count = 20; //当一个变量申明并且直接赋值后,可以不指明它的类型,因为TS可以自己做类型推断,推断出这个count是number类型
let height: number;//当声明一个变量后,没有直接赋值,建议指明它的类型
let age: number = 10;
let weight: number = 75.6;
//字符串类型
let myname: string = "张三";
//布尔类型
let isOk: boolean = false;
//null类型
let snull: null = null;
//undefined类型声明
let undefin: undefined = undefined;
//可以把undefined赋值给void类型,但是没有任何意义
let unusable: void = undefined;
//数组:第一种写法
let arr1: number[] = [1, 2, 3];
//数组:第二 种写法
let arr2: Array<number> = [1, 2, 4];
let arr3: Array<string> = ["a", "b", "c"];
2、TS新增类型的声明
元组 | any
//元组:元组类型表示允许一个已知元素数量和类型的数组,各个元素的类型可以不相同,也可以相同。
let arr: [number, number, string, boolean] = [1, 2, "abc", true];
//any:任意类型,可以表示为任意类型,未标注类型时默认为此类型。
let temp: any;
temp = 123;
temp = false;
temp = "abc";
枚举
//枚举:用enum关键字来声明枚举,枚举成员是有值的,默认为从0开始自增的数值,
// 我们把枚举成员的值为数字的枚举称为:数字枚举
enum Direction1 {
Up, //Up的默认为从0开始自增的数值,所以它的值默认为0 它相当于Up=0
Down, //它相当于Down=1
Left, //它相当于Left=2
Right, //它相当于Right=3
}
//用法:
console.log(Direction1.Up); //输出:0
enum Direction2 {
Up = 2, //Up的默认为从0开始自增的数值,但是此处我们已经指定了它的值为2
Down, //它相当于Down=3
Left, //它相当于Left=4
Right, //它相当于Right=5
}
enum Direction3 {
Up = 2, //我们也可以手动指定枚举的值
Down = 4,
Left = 6,
Right = 8,
}
//字符串枚举:枚举成员的值为字符串的时候此枚举为字符串枚举,
//字符串枚举没有自增长的行为,因此字符串枚举的每个成员必须要有初始值
enum Direction4 {
Up = "上",
Down = "下",
Left = "左",
Right = "右",
}
//用法:
console.log(Direction4.Up); //输出:上
//案例:用法
function changeDirection(direction: Direction4): void {
console.log(direction);
}
changeDirection(Direction4.Down) //输出:下
枚举的原理:
字面量类型
let str1 = "Hello TS"; //str1的类型为:string
const str2 = "Hello TS"; //str2的类型为:"Hello TS"
let str1: 18 = 18; //当把str1的类型指定为数值类型的18后,那么它的值就只能为18了,不能为19或者其他,它相当于const str1 = 18;
let str2: "中国" = "中国"; //当把str2的类型指定为字符串类型的"中国"后,那么它的值就只能为"中国"不能为其他的值了,它相当于const str2 = "中国";
let str3: true = true; //当把str3的类型指定为布尔类型的true后,那么它的值就只能为true,不能为false ,它相当于const str32: true = true;
字面量类型的用处:
使用模式:字面量类型一般配合联合类型一起使用。
使用场景:一般用来表示一组明确的可选值列表
比如:在贪吃蛇游戏中,游戏的方向的可选值只能是上,下,左,右中的任意一个值(它的用处和枚举很类似)
function changeDirection(direction: "上" | "下" | "左" | "右") {
console.log("您按了" + direction);
}
changeDirection("上") //输出:您按了上
相比string类型,字面量类型更加精确,严谨 ,不给你传错值的机会,固定死了你可以传那些值。
3、类型别名
使用type关键字创建类型别名
//类型别名: 类型别名其实就是自己先定义好一个类型,后续有其他变量要使用这个类型的时候,直接用了,避免每次写重复的类型代码:
type CustomArry = (string | number | boolean)[];
let arr1: CustomArry = [1, 2, "a"];
let arr2: CustomArry = ["a", "b", "c", 1, 2, 3, false];
//类型别名的作用是当存在多个变量使用同一个类型的时候,避免每次都写重复很长的数据类型 如:(string | number | boolean)[]
// let arr1: (string | number | boolean)[] = [1, 2, "a"];
// let arr2: (string | number | boolean)[] = ["a", "b", "c", 1, 2, 3, false];
//用type关键字定义一个Person类型别名
type Person = {
name: string;
age: number;
[key: string]: any; // 任意属性:即这个key可以是任意值的字符串
};
//用法
let user: Person = {
name: "Alice",
age: 25,
job: "Engineer",
};
4、对象类型
1、Object,object对象
//Object:大Object 代表所有拥有 toString、hasOwnProperty 方法的类型
//所以所有原始类型、非原始类型都可以赋给 Object(严格模式下 null 和 undefined 不可以)
let obj1: Object = {};
let obj2: Object | null = null; //联合类型:表是obj2既可以是object类型也能是null类型
let obj3: Object;
obj3 = 1; // 编译正确
obj3 = "a"; // 编译正确
obj3 = true; // 编译正确
obj3 = {}; //编译正确
//obj3 = null; // 报错
//obj3 = undefined; // 报错
//object:小object 类型用于表示所有的非原始类型,即我们不能把 number、string、boolean、symbol等 原始类型赋值给 object。
//在严格模式下,null 和 undefined 类型也不能赋给 object。
let obj4: object;
obj4 = {}; // 编译正确
//obj4 = 1; // 报错
//obj4 = "a"; // 报错
//obj4 = true; // 报错
//obj4 = null; // 报错
//obj4 = undefined; // 报错
2、自定义对象
在 TypeScript 中,对象是非常灵活的数据结构,你可以使用它们来组织和管理你的应用程序中的数据和行为。通过使用类、接口、类型别名和工厂函数,你可以创建出结构清晰、易于维护的代码。
//1、声明一个kitty的空对象: 此处的{}表示对象。而对象中没有属性或方法时,称为:空对象。
let kitty = {};
//2、声明一个person对象,对象包含了两个属性name和age,两个方法sayHello和myInfo
let person: {
name: string;
age: number;
sayHello(): void; //没有返回值的方法
myInfo(msg: string): string; //返回值为string类型的方法
};
person = {
//给person对象赋值
name: "张三",
age: 18,
sayHello() {
console.log(`我叫${this.name},我会唱歌`);
},
myInfo(msg: string) {
return `我叫${this.name},我今年${this.age}岁,我会${msg}`;
},
};
//用法:
person.sayHello(); //输出:我叫张三,我会唱歌
console.log(person.myInfo("跳舞")); //输出:我叫张三,我今年18岁,我会跳舞
//3、对象定义方式一:使用对象字面量语法直接创建一个user 对象,并赋值
let user = {
name: "张三",
age: 18,
gender: "男",
speak: function () {
console.log(`Hi, my name is ${this.name}.`);
},
};
//4、使用工厂函数创建对象
//先用type关键字来定义一个对象类型
type PersonType = {
firstName: string;
lastName?: string;
greet: () => string;
};
//利用工厂函数返回一个对象,它可以用于封装对象的创建逻辑
function createPerson(firstName: string, lastName: string): PersonType {
return {
firstName,
lastName,
greet() {
return `你好,我叫 ${this.firstName} ${this.lastName}`;
}
};
}
//用法
let myperson = createPerson("张","三");
console.log(myperson.greet()); // 输出 "你好,我叫张三"
//5、对象定义方式:构造函数+原型方法
function Users(this: any, name: string, age: number, gender: string): void {
//为什么要在参数中加this:any,如果不加这一句就会报TS2683错误
this.name = name;
this.age = age;
this.gender = gender;
}
Users.prototype.speak = function () { //给Users对象扩展一个speak方法
console.log(`Hi, my name is ${this.name as any}.`);
};
let myuser = new (Users as any)("张三", 18, "男"); //为什么要写成:new (Users as any)("张三", 18, "男");?为什么不直接写成:new Users("张三", 18, "男"); 如果不这样写会报:TS7009错误
myuser.speak();
3、TS中的类型运算符:typeof
let str = "Hello TS";
console.log(typeof str);
//根据已有变量的值,获取该类型
let p = { x: 1, y: 2 };
function formatPoint(point: typeof p) {
//根据typeof关键字来获取p的类型。point的类型就是p的类型
console.log(point.x);
console.log(point.y);
}
//用法:
formatPoint(p); //输出:1 2
formatPoint({ x: 3, y: 4 }); //输出:3 4
四、函数
1、普通函数
//函数声明的方式:定义一个名称为add的函数:带两个类型为number类型的参数,返回值为string类型
function add(num1: number, num2: number): string {
return (num1 + num2).toString();
}
//函数表达式的方式:定义一个名称为add的函数:带两个类型为number类型的参数,返回值为string类型
const add2: (num1: number, num2: number) => string = (num1, num2) => {
return (num1 + num2).toString();
};
//函数声明的方式:定义一个名称为message的函数,带一个类型为string的msg参数,没有返回值(void)
function message(msg: string): void {
console.log(`您收到最新消息:${msg}`);
}
//函数声明的方式:定义一个名称为getName的函数,带两个类型为string类型的参数,其中参数lastName可传可不传,返回值为string类型
function getName(firstName: string, lastName?: string): string {
if (lastName == undefined) {
return firstName;
}
return firstName + lastName;
}
//函数声明的方式:定义一个名称为axiosRequest的函数,带一个名称为config的参数,参数类型是一个对象{ url: string; method?: string },对象的method属性可传可不传
function axiosRequest(config: { url: string; method?: string }): void {
console.log("发起异步请求")
}
2、泛型函数
//声明一个名称为getVal的泛型函数,函数参数arg为T类型,函数的返回值为T类型
function getVal<T>(arg: T): T {
return arg;
}
let age: number = getVal<number>(18);
console.log(age); //输出 18
let myname: string = getVal<string>("张三");
console.log(myname); //输出 张三
五、接口
接口声明后,直接使用接口名称作为对象的类型
当一个对象类型被多次使用时, ,一般使用接口(interface)来描述对象的类型,达到复用的目的
接口只能给对象指定类型
接口声明后,直接使用接口名称作为对象的类型
//定义一个名称为IAnimal接口
interface IAnimal {
name: string;
}
//定义一个名称IKitty的接口,它继承了IAnimal接口 (extends关键字用于接口的继承。表示IKitty继承IAnimal。那么此时IKitty中除了有自己定义的ability属性外,还多了一个从IAnimal接口中继承来的name属性)
interface IKitty extends IAnimal {
ability: string;
}
//用法:声明一个名称为mykitty的对象,并指明了它是Kitty类型(这里的接口IKitty就是用来给对象mykitty指定类型的)
let mykitty: IKitty = { name: "小猫", ability: "爬树" };
接口与类型别名的区别
相同点:接口与类型别名都可以为对象指定类型
不同点:接口只能给对象指定类型,但是类型别名可以给任意类型指定别名
例如:
//类型别名可以为任意类型指定类型,而在这里接口则不行,接口只能为对象指定类型
type Animal = number | string;
let kitty1: Animal = 14;
let kitty2: Animal = "14";
六、类
类的基本使用:
class Person {
name = "张三"; //直接赋值了,TS可以自动类型推断出name是string类型,相当于:name:string="张三"
age: number; //因为age没有赋初始值,所以建议给它指定类型,否则它的类型就是any类型
//TS中用constructor关键字作为类的构造函数,构造函数是没有返回值的,所以不能指定返回类型
//类可以有构造函数,也可以不用构造函数
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
//类中的方法
hello(spe: string) {
console.log(`大家好,我叫${this.name},我今年${this.age}岁,我会${spe}`)
}
}
//用法:
const p = new Person("李四", 18);
p.hello("唱歌"); //输出:大家好,我叫李四,我今年18岁,我会唱歌
类的继承