一、TS文件运行前的准备
1.1、node的安装
你可以到 Node.js
官网去下载 Node 进行安装(https://node.js.org),建议你下载LTS
版本,也就是长期支持版本。安装的过程我就不演示了,这个过程就和安装 QQ 一样,没有任何难度。
如果你已经安装了,可以打开命令行工具,然后使用node -v
命令查看安装的版本,但是一般还有一个命令需要检测一下,就是npm -v
,如果两个命令都可以输出版本号,说明你的 Node 安装已经没有任何问题了。
1.2、TS的安装
npm install typescript -g
注意
:mac在执行这段命令的时候报错如下:
npm WARN checkPermissions Missing write access to /usr/local/lib/node_modules
npm ERR! code EACCES
npm ERR! syscall access
使用了sudo cnpm install typescript -g
执行就安装成功了
1.3、ts-node 的安装
npm install -g ts-node
报错的话就使用sudo cnpm install -g ts-node
安装
以上都安装成功的话就可以开始写代码,运行代码啦~(ts-node demo.ts
)
二、基础静态类型和对象类型
2.1、基础静态类型
基础静态类型非常简单,只要在声明变量的后边加一个:号,然后加上对应的类型哦。比如下面的代码,就是声明了一个数字类型的变量,叫做count。
let count: number = 1;
//这是一个number类型的
null
,undefinded
,symbol
,boolean
,void
这些都是最常用的基础数据类型
//any任意类型:在定义的时候没有定义类型,则该变量就是any类型
let age:any='12',
age = '我爱你中国'
2.2、对象类型
- 对象类型
const xiaoJieJie: {
name: string,
age: number,
} = {
name: "大脚",
age: 18,
};
console.log(xiaoJieJie.name);
- 数组类型
const xiaoJieJies: String[] = ["谢大脚", "刘英", "小红"];
- 类类型
const dajiao: Person = new Person();
- 函数类型
const jianXiaoJieJie: () => string = () => {
return "大脚";
};
自定义对象类型
interface XiaoJieJie {
uname: string;
age: number;
}
const xiaohong: XiaoJieJie = {
uname: "小红",
age: 18,
};
2.3、类型注解和类型推断
- 类型注解(type annotation)
let count: number;
count = 123;
//这段代码就是类型注解,意思是显示的告诉代码,我们的count变量就是一个数字类型,这就叫做类型注解
- 类型推断(type inference)
let countInference = 123;
//这时候我并没有显示的告诉你变量countInference是一个数字类型,但是如果你把鼠标放到变量上时,你会发现 TypeScript 自动把变量注释为了number(数字)类型,也就是说它是有某种推断能力的,通过你的代码 TS 会自动的去尝试分析变量的类型。
注意:
如果 TS 能够自动分析变量类型, 我们就什么也不需要做了
如果 TS 无法分析变量类型的话, 我们就需要使用类型注解
三、函数
3.1、函数的参数以及返回值
- 函数有返回值
// 参数要指定类型、返回值也要指定类型
function getTotal(one: number, two: number): number {
return one + two;
}
const total = getTotal(1, 2);
- 函数没有返回值
//没有返回值的函数,我们就可以给他一个类型注解void,代表没有任何返回值。
//如果这样定义后,再加入任何返回值,程序都会报错。
function sayHello(): void {
console.log("hello world");
}
- never返回值类型
//如果一个函数是永远也执行不完的,就可以定义返回值为never(执行的时候,抛出了异常,这时候就无法执行完了)
function errorFuntion(): never {
throw new Error();
console.log("Hello World");
}
//一直循环,也是我们常说的死循环,这样也运行不完
function forNever(): never {
while (true) {}
console.log("Hello JSPang");
}
- 函数参数为对象(解构)
//如果参数是对象---对象解构
function add({ one, two }: { one: number, two: number }): number {
return one + two;
}
const three = add({ one: 1, two: 2 });
//如果参数是对象,并且里边只有一个属性
function getNumber({ one }: { one: number }): number {
return one;
}
const one = getNumber({ one: 1 });
四、数组
4.1、一般数组类型的定义
const numberArr = [1, 2, 3];
//如果你要显示的注解,也非常简单,可以写成下面的形式。
const numberArr: number[] = [1, 2, 3];
//如果你的数组各项是字符串,你就可以写成这样。
const stringArr: string[] = ["a", "b", "c"];
//也就是说你可以定义任意类型的数组,比如是undefined。
const undefinedArr: undefined[] = [undefined, undefined];
//既有数字类型,又有字符串的时候
const arr: (number | string)[] = [1, "string", 2];
4.2、数组中对象类型的定义
const xiaoJieJies: { name: string, age: Number }[] = [
{ name: "刘英", age: 18 },
{ name: "谢大脚", age: 28 },
];
//上面的写法太麻烦,修改后
type Lady = { name: string, age: Number };
const xiaoJieJies: Lady[] = [
{ name: "刘英", age: 18 },
{ name: "谢大脚", age: 28 },
];
//上述写法在对象里再加入一个属性,编译器就会报错,修改为用类来定义
class Madam {
name: string;
age: number;
}
const xiaoJieJies: Madam[] = [
{ name: "刘英", age: 18 },
{ name: "谢大脚", age: 28 },
];
4.3、元祖的使用和类型约束
- 元祖的了解
可以把元组看成数组的一个加强,它可以更好的控制或者说规范里边的类型
const xiaojiejie: (string | number)[] = ["dajiao", 28, "teacher"];
//调换数组中元素的位置,如下,ts不能检测到问题
const xiaojiejie: (string | number)[] = ["dajiao", "teacher", 28];
//这时,需要元祖来加强
const xiaojiejie: [string, string, number] = ["dajiao", "teacher", 28];
//把数组中的每个元素类型的位置给固定住了,这就叫做元组。
五、TypeScript 中的 interface 接口
5.1、interface接口的理解和使用
个人理解: 从js的角度看ts中的interface,就是先声明了一个对象
obj
,这个对象obj
里面有很多的属性name
,这个对象obj
会在你后面定义的方法fun
中当作参数(形参)传进去,这个对象obj
中的属性有必传的、非必传的、还有函数类型的。你在方法fun
中若要使用某个属性name
,那么这个属性name
需要在对象obj
中定义过,否则会报错。(除非interface中定义了[propname: string]: any;
属性)
注意:对象中的每一个属性都需要定义类型,方法定义返回值的类型
// 这是定义的一个obj
interface Girl {
// 每一个属性都要有类型
name: string;
age: number;
// 必传的属性
height: number;
// 非必传的属性
footer?: number;
// 允许加入任意值
// 在实参中新增了sex属性,interface中并没有定义,此时并没有报错
[propname: string]: any;
// 存方法,要定义返回值的类型
say(): string;
//非必传方法
run?(): void;
}
// 后面定义的方法 fun 把上面定义的对象 Girl 当作参数传给方法
const screenResume = (girl: Girl) => {
girl.age < 24 && girl.height >= 168 && console.log(girl.name + "进入面试");
girl.age > 24 || (girl.height < 168 && console.log(girl.name + "你被淘汰"));
};
const getResume = (girl: Girl) => {
console.log(girl.name + "年龄是:" + girl.age);
console.log(girl.name + "身高是:" + girl.height + "cm");
girl.footer && console.log(girl.name + "鞋码是:" + girl.footer + "码");
girl.sex && console.log(girl.name + "性别是:" + girl.sex);
console.log(girl.name + "开心说:" + girl.say());
};
//实参
const girl = {
name: "大脚",
age: 18,
height: 174,
footer: 38,
// 这里不报错的原因是 interface中增加了 [propname: string]: any;属性
sex: '女',
say() {
return "hello world";
},
};
//调用方法
screenResume(girl);
getResume(girl);
控制台输出的内容:
大脚进入面试
大脚年龄是:18
大脚身高是:174cm
大脚鞋码是:38码
大脚性别是:女
大脚开心说:hello world
5.2 implements --- 接口(interface)的引用(被class引用)
// implements 是指 类 引用了 某个 接口(interface)
//这里是指 XiaoJieJie这个类 引用了 Girl 这个接口,此时定义XiaoJieJie这个类的时候,要给必传属性赋值
class XiaoJieJie implements Girl {
name = '小花';
age = 18;
height = 180;
say() {
return "傻子"
}
}
//new XiaoJieJie() ----实例对象
screenResume(new XiaoJieJie())
getResume(new XiaoJieJie())
控制台输出的结果:
小花进入面试
小花年龄是:18
小花身高是:180cm
小花开心说:傻子
5.3、extends --- 接口的继承
// extends 是指 新的接口 继承了 某个接口
interface Boy extends Girl {
play(): string;
}
const boy = {
name: 'wanwan',
age: 18,
height: 185,
say() {
return "是你呀";
},
play() {
return "打篮球";
}
}
const getResume = (girl: Boy) => {
console.log(girl.name + "年龄是:" + girl.age);
console.log(girl.name + "身高是:" + girl.height + "cm");
girl.footer && console.log(girl.name + "鞋码是:" + girl.footer + "码");
girl.sex && console.log(girl.name + "性别是:" + girl.sex);
girl.say() && console.log(girl.name + "开心说:" + girl.say());
girl.play() && console.log(girl.name + "喜欢的运动是:" + girl.play());
};
getResume(boy);
控制台结果:
wanwan年龄是:18
wanwan身高是:185cm
wanwan开心说:是你呀
wanwan喜欢的运动是:打篮球
5.4、函数型接口
因为对象中仅包含一个函数,这个对象的全部意义也仅在于那个可被外部调用的函数,故而称之为函数型接口。
六、TypeScript 中类的概念和使用
6.1、类的概念
使用关键字
class
// 定义一个类
class Lady {
content = "Hi,帅哥";
sayHello() {
return this.content;
}
}
// 实例类的对象
const goddess = new Lady();
console.log(goddess.sayHello());
6.2、extends --- 类的继承
// 定义一个类
class Lady {
content = "Hi,帅哥";
sayHello() {
return this.content;
}
}
// XiaoJieJie类身上既有自己的方法,也有从Lady类继承来的方法
class XiaoJieJie extends Lady {
sayLove() {
return "I love you";
}
}
console.log(new XiaoJieJie().sayHello());//Hi,帅哥
console.log(new XiaoJieJie().sayLove());//I love you
6.3、类(继承的子类)的重写和super关键字
// 定义一个类
class Lady {
content = "Hi,帅哥";
sayHello() {
return this.content;
}
}
// XiaoJieJie类身上既有自己的方法,也有从Lady类继承来的方法
class XiaoJieJie extends Lady {
sayLove() {
return "I love you";
}
// 可以对继承来的方法进行重写
sayHello() {
return "hi, wan";
}
// 继续使用Lady中的方法
ladySayHello() {
return super.sayHello() + "。你好!";
}
}
console.log(new XiaoJieJie().sayHello());//hi, wan
console.log(new XiaoJieJie().sayLove());//I love you
console.log(new XiaoJieJie().ladySayHello());//Hi,帅哥。你好!
6.4、类的访问类型 --- private、protected、public
- public
public从英文字面的解释就是公共的或者说是公众的,在程序里的意思就是允许在类的内部和外部被调用.
- private
private 访问属性的意思是,只允许再类的内部被调用,外部不允许调用
- protected
protected 允许在类内及继承的子类中使用
class Person {
protected name:string;
private age: number;
public sayHello(){
// 在类中使用 private 和 protected 属性都不会报错
console.log(this.name + 'say Hello' + ",年龄是:" + this.age) //此处不报错
}
}
//-------以下属于类的外部--------
const person = new Person()
//此处报错---属性“name”受保护(protected),只能在类“Person”及其子类中访问。
person.name = 'jspang.com'
//此处报错---属性“age”为私有属性(private),只能在类“Person”中访问。
person.age = 12
person.sayHello()
//此处报错---属性“name”受保护,只能在类“Person”及其子类中访问。
console.log(person.name)
6.5、类的构造函数
- 1、通过构造函数给类增加属性
类的构造方法只在实例化的时候被调用,并且只调用一次。
类的构造方法在类的外部无法访问。
- 2、调用构造方法中定义的属性
- 3、构造函数的访问控制符
class Person{
// 在构造函数中定义属性,必须给属性添加访问控制符(访问类型)
constructor(public name: string){
this.name = name
console.log(this.name);
};
public sayHello(){
// 这里的name需要通过this.name去获取,直接写name获取不到
console.log(this.name+':hello world')
}
}
let girl = new Person("xiaohua");//xiaohua(实例化的时候构造函数被调用)
// 调用构造方法中定义的属性
girl.sayHello();//xiaohua:hello world
console.log(girl.name); //xiaohua
6.5、继承类的构造器写法
子类中使用构造函数需要用super()调用父类的构造函数
// 父类
class Person{
// 在构造函数中定义属性,必须给属性添加访问控制符(访问类型)
constructor(public name: string){
this.name = name
console.log(this.name);
};
public sayHello(){
// 这里的name需要通过this.name去获取,直接写name获取不到
console.log(this.name+':hello world')
}
}
// 子类
class Teacher extends Person{
constructor(public age: number){
// 若父类的构造函数有参数则子类在使用super()时也需要传递参数否则会报错(2)
// 1、未写super时报错---派生类的构造函数必须包含 "super" 调用
// 2、未写参数时报错---应有 1 个参数,但获得 0 个;未提供 "name" 的自变量。
super("小花")
// 若父类的构造函数没有参数则子类在使用super()时不需要传参数
this.age = age;
console.log(this.name + "今年" + this.age + "岁")
}
}
let girl = new Person("xiaohua");//xiaohua(实例化的时候构造函数被调用)
// 调用构造方法中定义的属性
girl.sayHello();//xiaohua:hello world
console.log(girl.name); //xiaohua
let teach = new Teacher(24);//小花今年24岁(实例化的时候构造函数被调用)
6.6、类的 getter 、 setter 、static
- getter 、 setter
getter和setter都是属性,用get()和set()来设置private类型的属性可修改和获取
// set和get方法---用来修改private属性的值
class Person {
private _name: string;
constructor(private _age: number) {
console.log(this._name + "今年" + this._age + "岁")
}
get name() {
return this._name
}
set name(value) {
this._name = value
}
get age() {
return this._age - 10
}
set age(age: number) {
this._age = age
}
}
const girl = new Person(18)//undefined今年18岁
girl.name = "小花"
girl.age = 23
console.log(girl.name + "今年" + girl.age + "岁")//小花今年13岁
- 类中的static
当你不想new出对象,而想直接使用这个方法,用static声明的属性和方法,不需要进行声明对象,就可以直接使用
readonly只读属性
class Person {
public static logo: String = "huahua";
// readonly只读属性
public readonly xingm: string;
static sayLove() {
return "I Love you " + this.logo;
}
}
new Person().xingm = 'www';//报错 --- 无法分配到 "xingm" ,因为它是只读属性。
console.log(Person.sayLove());//I Love you huahua
- 抽象类---abstract
抽象类--- 用abstract 修饰, 里面可以没有抽象方法。但有抽象方法(abstract method)的类必须声明为抽象类(abstract class)
abstract class Development {
//抽象方法 ,不包含具体实现,要求子类中必须实现此方法
abstract skill():any;
//非抽象方法,无需要求子类实现、重写
equipment(){
console.log('非抽象方法,不要子类实现、重写');
}
}
// 多态:父类定义一个方法不去实现,让继承它的子类去实现 每一个子类有不同的表现
// 注意:使用多态基础是类的继承或者接口实现
class Frontend extends Development {
//子类中必须实现父类抽象方法,否则ts编译报错
skill() {
console.log("我是一个前端")
}
}
class Backend extends Development {
skill() {
console.log("我是一个后端")
}
}
class UI extends Development {
skill() {
console.log("我是一个UI")
}
}
七、联合类型和类型保护
- 1、联合类型(Union Types)可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值。
- 2、类型保护
1、类型断言就是通过断言的方式确定传递过来的准确值(传递过来的值中是否存在某个准确的值)
2、in语法
3、typeof语法
4、instanceof语法(只能用在类上)
个人:这一段理解比较重要,不知道怎么描述
八、枚举
使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript支持数字的和基于字符串的枚举。
// 随机选择一个数字
// function getServe(status: number) {
// if (status === 0) {
// return "这是0";
// } else if (status === 1) {
// return "这是1";
// } else if (status === 2) {
// return "这是2";
// }else if (status === 3) {
// return "这是3";
// }
// }
// const result = getServe(2);
// console.log(`输出:${result}`);
// const Stauts = {
// ZERO: 0,
// ONE: 1,
// TWO: 2,
// };
// function getServe(status: any) {
// if (status === Stauts.ZERO) {
// return "这是0";
// } else if (status === Stauts.ONE) {
// return "这是1";
// } else if (status === Stauts.TWO) {
// return "这是2";
// }
// }
// const result = getServe(Stauts.ONE);
// console.log(`输出:${result}`);
enum Stauts {
ZERO,
ONE,
TWO,
};
function getServe(status: any) {
if (status === Stauts.ZERO) {
return "这是0";
} else if (status === Stauts.ONE) {
return "这是1";
} else if (status === Stauts.TWO) {
return "这是2";
}
}
// const result = getServe(Stauts.TWO);//输出:这是2
const result = getServe(2);//输出:这是2
console.log(`输出:${result}`);
console.log(Stauts.TWO, Stauts[2]);//2 TWO
九、泛型
泛型:[generic - 通用、泛指的意思],那最简单的理解,泛型就是泛指的类型。
泛型的本质是参数化类型,通俗的将就是所操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法的创建中,分别成为泛型类,泛型接口、泛型方法。
泛型的定义使用<>(尖角号)进行定义
9.1、泛型的使用
// identity函数。 这个函数会返回任何传入它的值
// function identity(arg: number): number {
// return arg;
// }
// 这时identity函数就不可以传除number类型以外类型的参数
// console.log(identity(1))
// function identity(arg: any): any {
// return arg;
// }
// // 使用any类型会导致这个函数可以接收任何类型的arg参数,
// // 这样就丢失了一些信息:传入的类型与返回的类型应该是相同的(要自己typeof才会知道)
// console.log(identity(111))
// 泛型的使用
function identity<T>(arg: T): T {
return arg;
}
console.log(identity(111))
//STR:两个参数的类型相同
function join<STR>(first: STR, second: STR) {
return `${first}${second}`;
}
// 参数的类型可以在调用的时候定义
console.log(join<string>("我是", "huahua"))//我是huahua
console.log(join<number>(1,2))//12
9.2、泛型中数组的使用
// 泛型中 数组的使用
// 泛型函数loggingIdentity,接收类型参数T和参数arg,它是个元素类型是T的数组,并返回元素类型是T的数组
// 第一种写法
// function loggingIdentity<T>(agr: T[]){
// console.log(agr.length)
// }
// loggingIdentity<number>([1,2,3])
// 第二种写法
// function loggingIdentity<T>(agr: Array<T>){
// console.log(agr.length)
// }
// loggingIdentity<number>([1,2,3])
9.3、多个泛型的定义
// 多个泛型的定义
function joinMore<T, P>(first: T, second: P) {
return `${first}${second}`;
}
joinMore<number, string>(1, "2");
// 注意:如果函数定义了多个泛型,使用时要对应的定义出具体的类型。
9.4、泛型类
// class Person {
// constructor(private girls: string[]) {}
// getGirl(index: number): string {
// return this.girls[index]
// }
// }
// const girls = new Person(["huahua", "dahua", "xiaohua"]);
// console.log(girls.getGirl(1));//dahua
//此时若需要传入的数组是number类型的,上述代码则不能正常运行,此时使用泛型
class Person<T> {
constructor(private girls: T[]) {}
getGirl(index: number): T {
return this.girls[index]
}
}
const girls = new Person<string>(["huahua", "dahua", "xiaohua"]);
const boys = new Person<number>([1,2,3])
console.log(girls.getGirl(1));//dahua
console.log(boys.getGirl(1));//2
泛型类(好理解的方式---类的复用)
class GetMin<T>{
arr: T[] = [];
add(ele: T) {
this.arr.push(ele);
}
min(): T {
var min = this.arr[0];
this.arr.forEach(function (value) {
if (value < min) {
min = value;
}
});
return min;
}
}
var gm1 = new GetMin<number>();
gm1.add(5);
gm1.add(3);
gm1.add(2);
gm1.add(9);
console.log(gm1.min());
//比较字符在字母表中的索引
var gm2 = new GetMin<string>();
gm2.add("tom");
gm2.add("jerry");
gm2.add("jack");
gm2.add("sunny");
console.log(gm2.min());
9.5、泛型中类的继承
- 先看泛型中类的写法
class Person<T> {
constructor(private girls: T[]) {}
getGirl(index: number): T {
return this.girls[index]
}
}
const girls = new Person<string>(["huahua", "dahua", "xiaohua"]);
console.log(girls.getGirl(1));//dahua
上述写法中,getGirl方法只是获取了这个数组的第几项,在实际开发过程中,我们数组的每一项一般都是对象,
const girls = new Person([ { name: "huahua" }, { name: "dahua" }, { name: "xiaohua" }, ]);
此时需要获取这个对象的属性值(name),此时getGirl()的return就需要是this.girls[index].name
,但是此时会报错
此时,我们可以写一个接口(含有name属性),让类Person
继承这个接口
interface Girl {
name: string
}
class Person<T extends Girl> {
constructor(private girls: T[]) { }
getGirl(index: number): string {
return this.girls[index].name
}
}
const girls = new Person([
{ name: "huahua" },
{ name: "dahua" },
{ name: "xiaohua" },
]);
console.log(girls.getGirl(1));//dahua
9.6、泛型的约束
可以使用关键字extends来进行约束
class Person<T extends number | string> {
//.....
}
十、实际开发
自己百度吧,整理太麻烦
以上参考: https://jspang.com/detailed?id=63#toc290