TypeScript是什么
- TypeScript由微软开发的一款开源的编程语言
- TypeScript是JavaScript的超集,遵循最新的ES5/ES6规范,TypeScript拓展了JavaScript的语法。
- TypeScript更像后端的
Java、C#这样面向对象的语言,可以让JS开发大型企业应用。 - 越来越多的项目是基于
TS的,如果VSCode、Vue3、React16、Angular6 TS提供的类型系统可以帮助我们在写代码的时候提供更丰富的语法提示,代码提示,错误检查等等。- 在创建前的编译阶段经过类型系统的检查,就可以避免很多线上的错误
TS和JS有什么区别
- JS是动态编程语言,TS是静态编程语言
- JS是一边编译边运行,运行到错误的地方时才会提示。
- TS是先编译后运行,代码错误在写的时候就会提示。(TS不能直接执行,必须先编译成JS)
- TS是强类型语言,JS是弱类型语言。
- TS是JS的超集,JS有的TS都支持。
- TS的类型系统可以给我们提供强大的语法提示、错误检查、代码提示等等。
安装和编译
npm i typescript -g
npm i tsc -g
tsc helloword.ts 如果命令行报错tsc不是命令行可以使用这个方法npx tsc hellword.ts
实时编译
npx tsc --watch
vsCode运行ts脚本
vsCode运行ts,需要一个ts-node库,vsCode下载Code Runner插件,并且安装ts-node这个库不能全局安装,否则会报错。
yarn add -D ts-node 或者 npm i ts-node -D
切换至ts文件中,vsCode右上角点击这个图标就能运行TS文件,打印输出
TS的类型
布尔
let a:boolean = true
数字
let num:number = 13
字符串
let str:string = '小红'
数组
let ary:number[] = [1,2,3]
let ary2: (number | string)[] = [1, 2, 3, "xx"];
let arr:Array<number> = [3,4,5]
let arr2: Array<number | string | boolean> = [3, 4, 5, "ss", false];
元组类型
元组类型tuple,数量和类型已知的数组称为元组
let tuple:[number,string] = [1, 'xiaohong ']
枚举类型
普通枚举
enum Gender {
girl,
body
}
console.log(Gender.gril) //0
console.log(Gender.body) //1
编译以后转为以下代码
var Gender;
(function(Gender){
Gender[Gender['girl'] = 0] = 'gril'
Gender[Gender['body'] = 1] = 'boy'
})
所以我们可以通过
Gender[0] = 'gril'
Gender[1] = 'body'
Gender['gril'] = 0
Gender['body'] = 1
常量枚举
cosnt enum Color {
Red,
Yellow,
Blue
}
let myColor = [Color.red, Color.Yellow, Color.Blue]
编译后如下
let muColor = [0 /*它就相当于Color.Red */ , 1 /*相当于Color.Yellow*/, 2 /*相当于Color.blue*/]
任意类型 any
any可以赋值给任意类型,什么时候使用any类型,像数据过于过于复杂,数据类型转换的时候等等
如果变量定义为any类型,就跟js差不多,不进行类型检查
let root:any = document.getElementById("root")
如果要定义多个类型可以再类型后面用 `|` 隔开
let root:string | number = 125 | Boolean
console.log(root) //125
null 和 undefined 类型
他们两是其他类型的子类型,我们再声明变量为其他类型时,其变量的结果也可以等于null 或者 undefined
let x:number;
x = 1
x = undefined
x = null
但是如果tsconfig.json中,将strict属性设置为true,则不能把null undefined 赋值给x,如果赋值会报错。
非空断言
类似于js的?,比如let obj = {name: 'aaa'} let b = obj.age?.ary 如果不加?代码会报错,加上以后b就等于undefined
let element:HTMLElement | null = document.getElementById('root')
element!.style.color = 'red'
//上面的代码,如果不加!会报错。
nerver
nerver是null undefined的子集,代表不会出现的值
- 作为不会返回的返回值 类型,比如异常抛出,死循环没有返回值的情况,一般的函数如果没有
return默认返回undefined,不同于never等等。
function error(message: string): never {
throw new Error("报错了"); //直接异常结束了,不会执行log
console.log(11111);
}
- 作为不可能出现的情况
//可以通过类型进行判断
function error(message: string | number) {
if (message === "string") {
console.log(message);
} else if (message === "number") {
console.log(message + 1);
} else{
console.log(message) //never 绝对不可能走这
}
}
void
代表没有任何类型,void是undefined null的子集,但是如果在严格模式下,只有undefined能赋值,null
不行。
- 函数没有返回值,那么就是void类型
function a(): void {
return undefined;
}
void和nerver的区别
严格模式下,void可以赋值给undefined,但nerver不能赋值给任何类型
返回值为void的函数能正常执行,返回值为nerver的函数不能正常执行
Symbol
代表一个唯一的值
let a = Symbol('key')
let b = Symbol('key')
console.log(a===b) //false
BigInt
大数字类型,js能表示的最大数字,值为2的53次方-1 2*53-1值为90071992547409901,如果超过这个数计算就会产生误差,所以用大数字类型进行计算。
bigint和number不兼容,不能将bigint赋值给number,也不能将number赋值给bigint
let max = BigInt(Number.MAX_SAFE_INTEGER); //js能表示的最大数字,值为2**53-1
console.log(max + BigInt(1), max - BigInt(1));
//或者
console.log(max+1n,max-1n)
类型推导
当我们声明一个变量,没有给定类型时,第一次赋值TS会自动进行类型推导,意思就是赋值后,TS会根据你赋的值,判断这个变量为什么类型,后面就不能改变这个类型,否则会提示报错。
let userName = 'fsadf' //unserName已经是字符串类型了
userName = true //提示报错
包装对象 wrapper objefct
如果要原始类型调方法的时候,js会自动将你的原始数据类型包装成对象。例如string类型是没有toUpperCase这些方法的,而是String类才可以调用这些方法。
原始类型 对象类型
let name = 'fdasfsa'
console.log(name.toUpperCase()) //FDASFSA
//JS会做以下操作
console.log(new String(name).toUpperCase())
let isTrue1:boolean = true
let isTrue2:boolean = Boolean(1)
//如果是以下写法就会报错,提示不能将对象赋值给Boolean类型
let isTrue3:new Boolean(1)
联合类型
注意:联合类型进行赋值后,类型推导为赋值的类型,不可调用其他类型的方法,如果想调用其他类型的方法,则会报错。如果没有赋值,默认是第一个类型,下方代码默认是数字类型。
let userName: number | string;
userName = 3
console.log(userName!.toString());
userName = '你好';
console.log(userName.slice());
类型断言
使用(变量 as 类型)
let userName: number | string;
console.log((userName! as string)!.slice());
//如果将变量转为给其他类型,除去(any或者是unknown)。也会报错
console.log((userName! as boolean)!.slice())
//如果真的想转为其他类型 我们可以先将变量转为any或者unknown类型,使用双重断言进行转换
console.log((userName! as unknown as boolean));
(userName! as unknown as boolean) = false;
console.log(userName!); //false 这里如果不加非空断言会报错
字面量类型和类型字面量
字面量类型可以理解为自定义类型,可以实现枚举效果
const up: "Up" = "Up";
const down: "Down" = "Down";
const left: "Left" = "Left";
const right: "Right" = "Right";
type Direction = "Up" | "Down" | "Left" | "Right"; //这个也称字符串字面量
function move(Direction:Direction){
console.log(1111);
}
move(left)
move(555)//报错,只支持Direction中的类型
类型字面量
type Person = {
name: string,
age: number,
};
const p:Person ={
name:'xx',
age:45 //其中这个p变量缺少Person中任意一个属性都会报错
}
console.log(p); { name: 'xx', age: 45 }
字符串自变量和联合类型的区别
字符串字面量只能是定义的字符串类型且值为和类型一样,联合类型的值是只要符合定义的类型即可
type Direction = "Up" | "Down" | "Left" | "Right";
type T = string | number | boolean;
let d: Direction = "Up";
d = 666; //报错
let t: T = 8888;
函数
函数我们可以定义参数类型以及返回值类型,支持参数可选(可传可不传)用?:类型表示;支持参数赋默认值;支持剩余参数;
function fn(num: number, age?: number, name:string = 'xx'): number {
return num + age!; //这个要进行非空断言,否则会提示报错
}
console.log(fn(899, 89)); //988
function fn2(num: number, ...agr: number[]): number {
console.log(agr); //[89, 100, 56]
return num;
}
console.log(fn2(899, 89, 100, 56)); //899
函数的重载
两个或者两个以上的同名函数,参数不一样,在TS中,给同一函数,提供多个函数定义,叫函数的重载,写的时候需要注意,函数的声明和函数的实现必须紧挨着,中间不能写别的代码,否则会报错。
let obj: any = {};
/**
* @param val
* 如果val传的是一个字符串,赋值给obj.name,
* 如果传的是一个数字,赋值给obj.age
*/
function fn(val: string): void; //函数的定义
function fn(val: number): void; //函数的定义
//console.log(1111); //如果放开此行,会提示报错。
function fn(val: any): void { //函数重载的实现
if (typeof val === "string") {
obj.name = val;
} else if (typeof val === "number") {
obj.age = val;
}
}
fn("小狗");
fn(10);
fn(true); //提示报错,因为函数重载后,函数的实现中定义的参数类型必须符合函数的定义,true不属于函数重载定义中的参数类型
console.log(obj); //{ name: 'fsda', age: 10 }
什么情况下会使用函数的重载
当我们需要对函数的参数进行限制,如下代码。函数有两个参数,但是要求传参时,两个参数必须保持一致类型,则我们可以使用函数的重载进行限制。
let obj: any = {};
function fn(a: number, b: number): void;
function fn(a: string, b: string): void;
function fn(a: number | string, b: number | string): void {}
fn("小狗", "sdfa");
fn(10, 20);
fn("fsda", 50); //代码会提示报错,不符合函数重载的的定义
类的定义
如何定义类
注意:当我们在两个ts文件中,不能声明同名的类型,如果有就会提示报错,因为在ts文件中声明的类型都是全局的,即使是在不同文件声明。如果想不报错,我们则需要在将ts文件进行模块化,只要在ts文件中,出现export{}或者import,那这个ts文件里面的变量都会变成私有的。
export {};
class Person {
name!: string;
getName(): void {
console.log(this.name);
}
}
let p = new Person();
p.name = "小红";
p.getName(); //小红
定义存取器
export {};
class User {
myName!: string;
constructor(myName: string) { //构造函数
this.myName = myName;
}
get name() {
return this.myName;
}
set name(value) {
this.myName = value;
}
}
let user = new User("hhh"); //参数传给构造函数
user.name = "123456";
console.log(user.myName);
参数属性
上方的代码可以简化,利用这个public参数属性进行简化。这个参数属性相当于定义了一个参数变量的属性,并且在构造函数中,将参数赋值给参数变量,也就是注释掉的两行代码。
并且这个public参数属性本身的定义是公开的,这个属性的修饰符代表自己 自己的子类 以及其他类都可以访问这个属性。
export {};
class User {
// myName!: string;
constructor(public myName: string) { //构造函数
// this.myName = myName;
}
get name() {
return this.myName;
}
set name(value) {
this.myName = value;
}
}
let user = new User("hhh"); //参数传给构造函数
user.name = "123456";
console.log(user.myName);
readonly只读属性,属性前面加了readonly修饰符,这个属性只可以在构造函数修改,不支持别的地方进行修改,否则会报错。
export {};
class User {
// myName!: string;
readonly age!: string;
constructor(public myName: string) {
// this.myName = myName;
this.age = myName;
}
changeAge(age: string) {
this.age = age; //报错,age它只是只读属性
}
get name() {
return this.myName;
}
set name(value) {
this.myName = value;
}
}
let user = new User("hhh");
user.name = "123456";
user.age = 'ddd' //报错,age它只是只读属性
console.log(user.myName);
protected 受保护的,表示只有自己或者自己的子类能访问,其他地方不能访问。
export {};
class User {
constructor(public myName: string, protected age: number) {}
get name() {
return this.myName;
}
set name(value) {
this.myName = value;
}
}
class Student extends User {
stuNo!: number;
constructor(myName: string, age: number, stuNo: number) {
super(myName, age);
this.stuNo = stuNo;
}
}
let s = new Student("小红", 11, 1);
s.name = "校长";
console.log(s.age); //报错,age属性是受保护的
private 私有的,表示只能自己可以访问,自己的子类以及其他地方不能访问。
export {};
class User {
constructor(
public myName: string,
protected age: number,
private money: number
) {}
get name() {
return this.myName;
}
set name(value) {
this.myName = value;
}
getMoney(){
console.log(this.money) //这是自己可以访问
}
}
class Student extends User {
stuNo!: number;
constructor(myName: string, age: number, stuNo: number, money: number) {
super(myName, age, money);
this.stuNo = stuNo;
}
getArr() {
console.log(this.money); //报错 属性“money”为私有属性,只能在类“User”中访问 这是子类
}
}
let u = new User("hh", 10, 100);
console.log(u.money); //报错 属性“money”为私有属性,只能在类“User”中访问 这是实例
static 静态属性,静态属性继承时只继承给子类的,其他属性时继承给子类的实例
export {};
class User {
static userName: string = "userName";
constructor(
public myName: string,
protected age: number,
private money: number
) {}
get name() {
return this.myName;
}
set name(value) {
this.myName = value;
}
}
class Student extends User {
stuNo!: number;
constructor(myName: string, age: number, stuNo: number, money: number) {
super(myName, age, money);
this.stuNo = stuNo;
}
}
let s = new Student("夏普", 10, 0, 100);
console.log(Student.userName);//userName
console.log(s.userName);//报错 属性“userName”在类型“Student”上不存在。
继承
通过extends继承,字类将继承父类的属性以及方法,私有的除外。如果子类属性和父类属性同名,子类属性会覆盖父类属性。子类可以调用父类方法,父类调不了子类的方法。
export {};
class User {
constructor(public myName: string, public age: number) {}
get name() {
return this.myName;
}
set name(value) {
this.myName = value;
}
}
class Student extends User {
stuNo!: number;
constructor(myName: string, age: number, stuNo: number) {
super(myName, age);
this.stuNo = stuNo;
}
}
let s = new Student("小红", 11, 1);
s.name = "校长";
console.log(s); //{ myName: '校长', age: 11, stuNo: 1 }
装饰器
类装饰器
类装饰器在类声明之前声明,用来监视修改替换类的定义。