Typescript的学习
why tpyescript?
- 程序更容易理解
- 效率更高
- 在不同的代码块和定义中跳转
- 代码自动补全以及丰富的就接口提示
- 更少的错误
- 非常好的包容性,完美兼容
javascript
1.typescript的安装:
1.安装:
//安装
sudo npm install -g typescript
//检查
tsc -v //正常会弹出安装的TS版本号
2.卸载重装:
//卸载typescript
sudo npm uninstall -g typescript
//找到tsc目录,删除它
where tsc # /usr/local/bin/tsc
cd /usr/local/bin
rm -rf tsc
//重新安装
sudo npm install -g typescript
2.类型:
1.基础类型:
ECMAScript最新定义了8种数据类型:
7种原始数据类型- Boolean
- Null
- Undefined
- Number
- String
- Symbol
- BigInt
// 布尔类型
let isDone:boolean =true;
// 数字类型
let age:number =24;
// 字符串类型
let PlayerName:string = "艾弗森";
// undefined
let u:undefined =undefined;
// null
let n:null =null;
//number类型可以赋值undefined
let num:number = undefined;
undefined和null是所有类型的子类型,也就是说他俩可以赋值给任何类型的变量
2.any类型和联合类型:
any:可以赋值任意类型
// any类型:可以赋值任意类型
let notSure:any="任意类型any";
notSure=24;
notSure=true;
notSure.myname;
notSure.getName();
联合类型:a类型或者b类型...
// 联合类型:如下例子🌰可以是数字类型或者字符串类型
let NumorString:number | string =24;
NumorString="科比"
3.数组Array和元组tuple:
-
数组://数字数组 let arrofNumber:number[] = [1,2,3,4,5]; arrofNumber.push(520); -
类数组-IArguments://IArguments function test(){ console.log(arguments); console.log(arguments.length); //有length属性 console.log(arguments[0]); //可以使用[] let igr:IArguments = arguments; } -
元组:元组可以简单的理解为对数组的每项进行类型的规范的数组。//元组:限定了数组每一项类型的数组 let tuple:[string,number]=["韦德",3]; //✅ tuple=["韦德",3,"迈阿密热火队"]; //❎
3.Interface初探:
interface的主要作用:ts里的interface(接口)可以理解为定义一些参数,规定变量里面有什么参数,参数是什么类型,使用时就必须有这些对应类型的参数,少或者多参数、参数类型不对都会报错。
- 对
对象的形状进行描述 - 对
类进行抽象 Duck Typing鸭子类型(类型推测):如果一个东西能像鸭子🦆一样游泳,吃饭,遛弯,那么就可以称之为鸭子
例子代码:
?表示可选属性readonly表示只读,不可修改
interface Person{
name:string,
age?:number //?表示可选属性
readonly id:number //只能读,不能修改重写;再有readonly是用在属性上的
}
let kirk:Person={
name:"kirk",
age:18,
id:9527
}
let xiaogang:Person={
name:"小刚",
id:9275
}
kirk.id=1111; // ❎ readonly后不可修改
4.函数类型和类型推断:
1.函数类型:
冒号":"后面基本都是关于类型的解释冒号前 + ?:表示可选ts中,
//函数类型
function add(x:number,y:number,z?:number):number{
if(typeof z ==="number"){
return x+y+z;
}else{
return x+y;
}
}
let all=add(2,99,1);
console.log(all)
// 函数表达式
const add2:(x:number,y:number,z?:number)=>number = add;
使用interface描述函数:
interface ISum {
(x:number,y:number,z?:number):number
}
let add2:ISum = add;
2.类型推断:
虽然这里没有对str的类型进行约束,但是ts会自行推断str的类型为字符串,所以以后就不可以将他命名为其他类型的值
//ts的类型推断
let str="琳琅满目";
str=123 // ❎ 上面ts已经推断出str为字符串类型,不能再赋值为数字类型的
5.类class:
1.类class:
- 类(
class):定义了一切事物的抽象特点 - 对象(
object):类的实例 - 面向对象
oop三大特性:封装、继承、多态- 封装:将对
数据的操作细节隐藏起来,只暴露对外的接口;外接不需要知道对外的接口就能访问这个对象。 - 继承:子类继承父类,除了有自己的一些具体的特性外,还有父类的
- 多态:是由继承产生的相关的不同的类,对同一个方法有一个不同的响应。例如:猫和狗都继承动物,但是他们都分别实现了自己的吃的方法。
- 封装:将对
//创建一个动物的类(class.ts文件里的代码)
class Animal{
name:string;
// 构造函数
constructor(name:string){
this.name=name;
}
run(){
return `${this.name} is running~!`
}
}
const snake=new Animal("lisin");
console.log(snake.run()); //lisin is running~!
2.安装ts-node以及对代码运行:
//安装
sudo npm install -g ts-node
运行class.ts文件
ts-node class.ts
3.class.ts的代码:
class Animal{
name:string;
// 构造函数
constructor(name:string){
this.name=name;
}
run(){
return `${this.name} is running~!`
}
}
const snake=new Animal("lisin");
console.log(snake.run()); // lisin is running~!
// 继承
class Dog extends Animal{
bark(){
return `${this.name}is 汪汪汪~!`
}
}
const ahuang=new Dog("阿黄");
console.log(ahuang.bark()); //阿黄is 汪汪汪~!
//🌵多态:ahuang有自己的run方法了
console.log(ahuang.run()); //阿黄 is running~!
class Cat extends Animal{
constructor(name){
super(name);
console.log(this.name);
this.name=name;
}
run(){
return `一只叫 ${this.name} 的猫,正在和毛线球乱跑...`
}
}
const maomao=new Cat("毛毛");
console.log(maomao.run()); //一只叫 毛毛 的猫,正在和毛线球乱跑...
6.修饰符:
三总修饰符:
public:被public修饰的是公有的,可以在类的外部被访问到。private:被private修饰的是私有的,只可以在自己的类下访问。protected:被protected修饰的,只可以在子类下和自己的类中访问到。(相当于把被修饰的部分变成了遗产,只有我和我的子女可以访问,其他人不行的。)
1.public修饰符:允许在类的外部访问
class Person {
public name:string;
constructor(name:string) {
this.name = name;
}
}
const kirk = new Person("krik");
console.log(kirk.name);//ts-node打印出 --- kirk
- 实例可以获得
内部的name属性,如果是public的话,那么name将不可以被访问到。
关于super:
class Person {
public name:string;
constructor(name:string) {
this.name = name;
}
}
const kirk = new Person("kirk");
console.log(kirk.name);
class Teacher extends Person {
constructor(public age:number) {
super("测试子类向父类传递的值") //这里的super会将值传递给Person类的constructor中去
}
}
const testName = new Teacher(99);
console.log(testName.age); // 99
console.log(testName.name);//测试子类向父类传递的值
- 另外这里有个地方强调的,
class继承时候,不传值,constructor中的super也是要写的哈
2.private修饰符:只能自己的类内访问
//private修饰符
class Animal{
private name:string;
// 构造函数
constructor(name:string){
this.name=name;
}
run(){
return `${this.name} is running~!`
}
}
class Cat extends Animal{
constructor(name){
super(name);
console.log(this.name); //由于上面的private修饰符,这里面会报错
this.name=name; //由于上面的private修饰符,这里面会报错+1
}
run(){
return `一只叫 ${this.name} 的猫,正在和毛线球乱跑...`//由于上面的private修饰符,这里面会报错+1
}
}
3.protected修饰符:只能自己和自己的孩子访问
//proteted修饰符
class Animal{
protected name:string; //这个地方是protected修饰符修饰
// 构造函数
constructor(name:string){
this.name=name;
}
run(){
return `${this.name} is running~!`
}
}
class Dog extends Animal{
// 构造函数
constructor(name:string){
super(name);
this.name=name;
}
run(){
return `${this.name} is swiming~!`
}
}
const dog=new Dog("kirk");
console.log(dog.run()); //支持自己和自己孩子访问的,结果如下👇
//kirk is swiming~!
const snake=new Animal("lisin");
snake.name="gogoing" //会报错,因为上面的protected修饰符的关系,现在的snake.name只能被在"Animal"以及子类中访问到。
console.log(snake.run()); // lisin is running~!
4.readonly修饰符:只读
//readonly修饰符
class Animal{
readonly name:string;
// 构造函数
constructor(name:string){
this.name=name;
}
run(){
return `${this.name} is running~!`
}
}
const snake=new Animal("lisin");
snake.name="gogoing" //会报错,因为上面使用了readonly修饰符,所以只能"读取",不能对其修改。
console.log(snake.run()); // lisin is running~!
5.static修饰符:(当类的状态和实例没有关系的时候,可以使用静态方法)
class Animal{
name:string;
static categories:string[]=["哺乳动物","鸟类"]
// 构造函数
constructor(name:string){
this.name=name;
}
run(){
return `${this.name} is running~!`
}
isAnimal(a){
return a instanceof Animal;
}
}
console.log(Animal.categories); //[ '哺乳动物', '鸟类' ]
const snake=new Animal("lisin");
console.log(snake.isAnimal(snake)); //true
7.interface和implement:
将类中共有的逻辑提取出来,放到interface中,然后通过implement来完成对interface的实现。
- 通过
interface去告诉Car和cellPhone,你们都要去实现这个Radio的方法。 interface接口之间是可以通过extends继承拓展的。class类是通过implements来对interface完成使用的。
//接口①
interface Radio{
switchRadio():void
};
//接口②
interface Battery{
checkBattery():void
};
//接口之间的继承
// 实现了Radio和Barrery方法,inerface也可以使用extends继承
interface RaidowithBattery extends Radio{
checkBattery():void
};
//class类对接口的使用
class Car implements Radio{
switchRadio(){
console.log("我是Car的SwithRadio方法");
}
}
// 对多个接口的implements
class CellPhone implements Radio,Battery{
switchRadio(){
console.log("我是cellPhone的SwithRadio方法");
}
checkBattery(){
console.log("我是检查电池🔋的")
}
}
class CellPhone1 implements RaidowithBattery{
switchRadio(){
console.log("我是cellPhone的SwithRadio方法");
}
checkBattery(){
console.log("我是检查电池🔋的")
}
}
const pokmen = new CellPhone();
console.log(pokmen.switchRadio()); //我是cellPhone的SwithRadio方法
8.枚举:
1.enums:
- 枚举是一个
双向的映射
enum Direction{
Up,
Down,
Left,
Right
};
//🤖这种双向访问有点牛皮
console.log(Direction.Down); // 1
console.log(Direction[3]); //Right
2.编译后文件的理解:
例子1:
//enmus.ts文件
enum Direction{
Up=10, //赋值up为10后,后面的down、left、right依次递增
Down,
Left,
Right
};
console.log(Direction.Down); //11
console.log(Direction[13]); //right
接着执行命令行进行编译:
tsc enums.ts
编译后的文件:
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 10] = "Up";
Direction[Direction["Down"] = 11] = "Down";
Direction[Direction["Left"] = 12] = "Left";
Direction[Direction["Right"] = 13] = "Right";
})(Direction || (Direction = {}));
;
console.log(Direction.Down); //11
console.log(Direction[13]); //Right
- 自执行函数,封装了一个自己的
独特的作用域 []内的Direction["Up"]=0是用于自我解释的,会将Direction对象里面的Up设置为0;javascript赋值操作返回的是被赋予的这个值,所以Direction[Direction["Up"] = 10] = "Up",相当于Direction[10] = "Up";
例子2:
编译前:
//enums.ts
enum Direction{
Up="UP",
Down="DOWN",
Left="LEFT",
Right="RIGHT"
};
console.log(Direction.Down); //1
console.log(Direction[3]); //Right
编译后:
var Direction;
(function (Direction) {
Direction["Up"] = "UP";
Direction["Down"] = "DOWN";
Direction["Left"] = "LEFT";
Direction["Right"] = "RIGHT";
})(Direction || (Direction = {}));
;
console.log(Direction.Down); //1
console.log(Direction[3]); //Right
例子3:
enum Direction{
Up="UP",
Down="DOWN",
Left="LEFT",
Right="RIGHT"
};
// console.log(Direction.Down); // 1
// console.log(Direction[3]); // Right
const value="DOWN";
if(value === Direction.Down){
console.log("CrossOver~!")
}
//打印结果
//CrossOver
9.泛型:
1.泛型:
- 在函数名称后面声明泛型变量
<T>,它用于捕获开发者传入的参数类型(比如说string),然后我们就可以使用T(也就是string)做参数类型和返回值类型了
//这个"<>"东西就是泛型
function echo<T>(arg:T):T
{
return arg;
}
//通过类型推断,T为字符串类型了
const result=echo("123");
//泛型在元组中的使用:
function swap<T,U>(tuple:[T,U]):[U,T]
{
return [tuple[1],tuple[0]]
}
const result2=swap(["字符串",123]);
//鼠标放到result2,看到result2的类型第一个是number,第二个是string
console.log(result2[1]); // 字符串
2.约束泛型:
//1.在T后面添加[]达到约束泛型的效果
function echoWithArr<T>(arg:T[]):T[]{
console.log(arg.length);
return arg;
}
const arrs=echoWithArr([23,34,45]);
//控制台打印结果:3
//2.使用interface来进行限制
interface IWithLength{
length:number
};
//🌵尖括号内写extends,约束函数内的传入值必须是拥有length属性的才可以
function echoWithLength <T extends IWithLength>(arg:T):T
{
console.log(arg.length);
return arg;
}
const o = echoWithLength("sdsfs");
const obj = echoWithLength({length:1005});
console.log(o); // 5
consple.log(obj);// 1005 {length:1005}
- 就是对
输入和输出进行限制---只有拥有length属性的,才可以,否则就报错- 在
T后面添加[]达到约束泛型的效果 - 通过
extends interface来进行限制
- 在
3.索引泛型:
我们要设计一个函数,这个函数接受两个参数,一个参数为对象,另一个参数为对象上的属性,我们通过这两个参数返回这个属性的值,比如:
function getValue(obj: object, key: string) {
return obj[key] // error
}
参数 obj 实际上是 {},因此后面的 key 是无法在上面取到任何值的。
下面这里引入索引类型:
function getValue<T extends object, U extends keyof T>(obj: T, key: U) {
return obj[key] // ok
}
keyof把传入的对象的属性类型取出生成一个联合类型。
10.泛型的应用:
1.下面的例子会有一个问题,无法判断是否能使用toFixed方法:
// 泛型在类中的应用例子1:
class Queue{
private data=[];
push(item){
return this.data.push(item);
};
pop(){
return this.data.shift();
}
}
const queue=new Queue(); //💡
queue.push(1);
queue.push("this Love")
console.log(queue); // Queue { data: [ 1, 'this Love' ] }
console.log(queue.pop().toFixed());// 1
console.log(queue.pop().toFixed());//this Love 是个字符串类型,无法使用toFixed方法,会报错;
2.最好的解决办法:
通过泛型来约束类的形状👇:
class Queue<T>{
private data=[];
push(item:T){
return this.data.push(item);
};
pop(){
return this.data.shift();
}
}
const queue=new Queue<number>(); //注意这个地方和上面例子的差别,多了<number>
queue.push(1);
queue.push("this Love")//这时候这里面就有提示了----类型“this Love”的参数不能赋值给number参数
const queue2=new Queue<string>();
queue2.push("pokmeon go!");
console.log(queue2.pop().length); //11
3.泛型在interface中的使用:
//定义接口
interface KeyValue<T,U>{
key:T;
value:U;
};
//使用接口,并用泛型对接口进行约束
let k1:KeyValue<number,string>={
key:920,
value:"小镇姑娘"
};
console.log(k1); //{ key: 920, value: '小镇姑娘'
let k2:KeyValue<string,number>={
key:"去吧,皮卡丘~!",
value:999
};
console.log(k2); //{ key: '去吧,皮卡丘~!', value: 999 }
- 数字数组的
2种写法:
let arr:number[]=[1,2,3,4,5,6];
//使用泛型方式定义数组
let arr1:Array<number>=[6,5,4,3,2,1];
console.log("我是arr",arr); //我是arr [ 1, 2, 3, 4, 5, 6 ]
console.log("我是arr1",arr1); //我是arr1 [ 6, 5, 4, 3, 2, 1 ]
//传入不同的泛型值可以适配不同的类型🌵
//定义一个用于约束函数的泛型接口
interface IPlus<T>{
(a:T,b:T):T;
}
function plus(a:number,b:number):number{
return a+b;
}
function connect(a:string,b:string):string{
return a+b;
}
const a:IPlus<number>=plus;
const b:IPlus<string>=connect;
11.类型别名和类型断言
1.类型别名:
//类型别名使用1:
type PlusType = (x:number,y:number)=>number;
function sum(x:number,y:number){
return x + y;
};
const sum2:PlusType = sum;
//类型别名使用2:
type NameReslover = () => string;
type NameOrReslover = string | NameReslover;
function getName(n:NameOrReslover):string
{
if(typeof n === "string"){
return n
}else{
return n();
}
}
案例2解析:
-
首先通过
type NameReslover =() => string; 制定了一个叫type NameOrReslover=string | NameReslover;规则,就是一个函数,返回的是一个字符串 -
然后通过
type NameOrReslover=string | NameReslover; 制定了一个叫NameOrReslover的规则,他可以是一个字符串或者之前上面定义的那个NameReslover -
创建一个
getName的函数,参数规定为NameOrReslover,返回值规定为string; -
这个叫
getName的函数,函数体内部判断参数的类型是否为"string",如果是string就return返回,如果不是就调用函数。 -
注意理解上面的类型别名
// 类型别名使用3:
type Directions = "Up" | "Down" | "Left" | "Right"
let toWhere:Directions = "Left";// 这里将toWhere限定在上下左右中的一个
2.交叉类型:
// 交叉类型例子:
interface IName {
name:string
}
type IPerson = IName & {age:number}
let person:IPerson = {name:"123",age:123}
3.类型断言:
类型断言是用来告诉编译器,自己比它更了解这个类型,并且让它不要发出错误,这个就是类型断言。
function getLength(input:string | number):number{
//常规操作
// const str = input as String;
// if(str.length){
// return str.length;
// }else{
// const number =input as Number;
// return number.toString().length;
// }
//骚气的断言操作
if((<string>input).length){ // (<string>input)这里就是断言操作了,注意要打括号
return (<string>input).length
}else{
return input.toString().length;
}
}
console.log(getLength(1231231));// 7
使用type guard改写上面方法:
type guard就是使用instanceof或者typeof等去缩小类型的范围
function getLength(input:string | number):number{
if(typeof input === "string"){
return input.length;
}else{
return input.toString().length
}
}
可以看出,使用断言操作后,相比较上面的常规操作要简洁很多。
12.声明文件和内置类型:
1.声明文件:
例如使用Jquery:
-
通常会把下面的声明放到一个单独的文件中去-
jQuery.d.ts -
声明文件一般用
文件名+.d.ts结尾
declare var jQuery:(selector:string) => any;
或者通过安装官方声明的文件:
npm install --save @types/jQuery
更多的一些第三方库的安装,以及声明文件可以查询一下地址:
www.typescriptlang.org/dt/search?s…
2.内置类型:
const a:Array<number> = [1,2,3];
const data = new Date();
cosnt reg = /abc/
reg.test("abc")
Math.pow(2,2)
let body = document.body;
let allLis = document.querySelectorAll("li");