携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
前言
最近在无意中看到了关于ts+vue3的项目,突然上手感觉很生疏,突然发现一直以来一直在写js,好像把之前的ts忘的差不多了,正好现在重新整理一下,分享给大家,本文比较简单,相对于看起来比较轻松,同时如有不足,欢迎指正。
一、认识TypeScript
1.回顾JavaScript
在正式开始认识TypeScript之前,我们需要回顾一下JavaScript,JavaScript是动态类型语⾔(Dynamically Typed Language),在运⾏期间检查数据的类型的语⾔。当然使⽤这类语⾔编程,不会给变量指定类型,⽽是在赋值时得到数据类型。
例如:
var a=123;//number类型
a='xiaomin';//字符串类型
a=true;//布尔类型
function fun(name,age){
console.log(name,age);
}
fun('xiaohong',123);
2.TypeScript特点
TypeScript是微软开发的⼀个开源的编程语⾔,通过在JavaScript的基础上添加静态类型定义构建⽽成。
- 是以
JavaScript为基础的语言 - 是
JavaScript的一个超集 - 完美的支持
JavaScript语法,并且扩展类型 TypeScript是一个静态类型的语言
3、TypeScript增加了什么?
- 类型
- 完美⽀持ES新特性
- 添加ES不具备的新特性
- 丰富的配置选项
- 强⼤的开发⼯具
4.TypeScript环境搭建
1、安装node.js
node.js下载地址: nodejs.org/en/download…
测试是否安装成功 : 进⼊命令⾏输入node -v
2.使⽤npm全局安装typescript
1 进入DOS界面
2 输⼊:npm i -g typescript
3 验证:tsc -v
3.将ts⽂件编译成js⽂件
ts的意思是 TypeScript Complie
tsc 被编译⽂件的名字.ts
二、TypeScript中简单的数据类型
1.类型声明
1)特点
- 类型声明是TS中⾮常重要的特点
- 通过类型声明可以
指定TS中变量(形参)的类型 - 指定类型后,当变量赋值时,TS编译器就会
⾃动检查值是否符合声明类型,如果符合就赋值,不符合就报错 - 简单的说,类型声明就是
给变量设置了类型。使得变量只能存储某种类型的值。
2)语法
//1 先声明后赋值
let a:number;//声明了变量b,指定了类型为number
a=123;//给b赋值123
// b='xdlcass'; //报错 b赋值了之后不能再被赋值其他类型了
//2 声明并赋值
let c:boolean=true;
console.log(b+'=b');//123
console.log(c+'=c');//true
// 3 形参声明类型
function fun(name:string){
console.log(name);
}
// fun(123);//报错
fun('miaozai');
//冒号后面能接受js的基本类型,也可以是复杂类型
2.自动类型判断
1)什么是⾃动类型判断?
- TS拥有
⾃动类型判断机制 - 当对变量的声明和赋值同时进⾏时,TS编译器会
⾃动判断变量类型 - 所以如果
变量的声明和赋值是同时进⾏时,可以省略掉类型声明
2)语法
let a:number;//声明未赋值
a=1;
let b=2;//声明变量并且赋值
b='miaozai';//此时会提示报错
console.log(typeof a);
console.log(typeof b);
3.字面量声明
1)字面量
字⾯量:是个客观存在⽆法改变的值
例如: 1 2 3 4 5 6 7 'hello' true
2)语法
let a:1;
// a=3;
const b=12;
4.TS中any类型
1)any类型
任意的数据类型,⼀个变量设置类型为any后相当于对该变量关闭了TS的类型检测
2)声明方式
(1)显式的any类型声明
let a:any;
a=1;
a='miaozai';
a=true;
(2)隐式的any类型声明
let b;
b=2;
b=true;
b='miaozai';
3)使⽤any存在的隐患
使⽤any声明的变量它不仅影响⾃⼰,同时还影响别⼈(不同类型的变量值赋值之后,也会改变被赋值的变量类型)
5.TS中unknown类型
1)unknow类型
未知类型的值;
2)unknown声明方式
let a:unknown;
a=1;
a='miaozai';
a=true;
let b:boolean;
// b=a;//这里直接赋值的话,Ts解析器式报错的 因为b是布尔类型的,而a为unknown
//解决上面的问题,只要先进行判断
if(typeof a ==='boolean'){
b=a;
}
console.log(b);
console.log(a);
3)注意:
unknown可看成安全的any
6.TS中的类型断⾔
1)类型断⾔
可以⽤来告诉TS解析器变量的实际类型
2)语法:
变量 as 类型;
<string> 变量;
3)处理赋值unknown问题
利⽤类型断⾔来处理unknown赋值的⼩问题
let a:string;
let b:unknown;
d="miaozai";
a=d as 'string';
7.TS中的函数
1)对函数中形参进⾏类型声明
语法:
function fun(形参1:number,形参2:number){
console.log(形参1+形参2);
}
//正确传参
fun(123,456);
2)函数中返回值类型的声明
(1)不设置返回值类型,但是有返回值(undefined)
function fun(){
return true;
}
//这个时候TS解析器会根据返回值的类型进⾏判断,返回值类型是什么函数返回值类型就是什么
let result=fun();
console.log(typeof result);
(2)设置返回值类型为void
//void:⽤来表示空,以函数为例,就表示没有返回值的函数
function fun():void{
return 'a';//提示报错
return 1;//提示报错
return true;//提示报错
return undefined;//不提示报错
return null;//不提示报错
return;//不提示报错
}
(3)设置返回值类型为 never
//never:永远不会返回结果
function fun():never{
throw new Error("出错了!!");
//程序报错代码⽴即结束,⽴即结束以后就不会有返回值了,这个东⻄⽤的相对⽐较少,了解⼀下知道⼀下就⾏了
}
三、TS中的复杂的数据类型声明
1.TS中的对象类型声明
1)声明例子
let a:object; //声明⼀个为类型为object(表示⼀个对象)的变量,也就是说a只能⽤来保存⼀个对象
let b:{name:string};//声明⼀个变量b,类型为对象,对象中只能有⼀个属性为名为name且属性值的类型为string
c={};//提示报错
c={name:'⼩明'};//这样写才正确
2)语法:
{属性名:属性值,属性名1:属性值1}
注:通过这种⽅式指定对象的时候,写的这个对象必须要这个要求指定对象的格式必须⼀模⼀样
3)实战解决
- 情景⼀:我们指定的对象它必须包含⼀个name,但是还有age和sex这个俩个变量是可能有可能有没有
let a:{
name:string,
age?:number,
sex?:string
}
a={
name:'miaozai',
age:2
}
- 情景⼆:当我们只知道我们必须有的name属性,⽽其他的不必须我们不知道,
let b:{
name:string,
[propName:string]:unknown//表属性名为string类型,为可选的并且这个属性的值为unknown类型
}
//[propName:string]:这个任意命名,就表示属性的名字,这个属性名字的类型是string。js中属性名⼀般都是⽤string定义
b={
name:'miaozai',
sayHello(){
console.log(this.name);
}
}
console.log(b.name);
if (typeof b.sayHello === 'function'){
b.sayHello();
}
2.TS中数组类型声明
1)第⼀种
语法:let 变量名:类型名[ ];
let a:string [ ]; //声明⼀个数组
string [ ]; //表示字符串数组
e=['a','b','miaozai',1]; //但是你存⼀个TS解析器就是提示报错
let b:number[ ];//number型的数组
let c:boolean[ ];//boolean型的数组
2)第二种
语法:let 变量名 : Array<类型名>
let b:Array<boolean>;
b[0]=false;
//创建⼀个能存储任意类型的数组
let a:Array<any>;
let b:any[ ];
3.TS中的扩展类型tuple
1)tuple:叫做元组
2)什么是元组?
元组就是定⻓的数组。(就代表数组中的数量是固定的就叫做元组,元组的存储效率⽐较好点,因为元组是固定,不会出现扩容的现象,所有效率会好点)
3)使用场景
就是数组的数量是固定的时候
4)元组的写法
let h:[string,string];//在这个元组中有两个,第⼀个值是string类型。第⼆个值也是string
//定义的时候多⼀个少⼀个也不⾏,必须按照声明的结构定义数组,不然TS解析器就会提示报错
h=['miaozai','yuanzi'];
`元组书写语法:[类型,类型,类型]`;//这个不会特别⻓,元素多的话还是⽤数组
4.TS中Enum
1)什么是enum(枚举)? 在数学和计算机科学理论中,⼀个集的枚举是列出某些有穷序列集的所有成员的程序,或者是⼀种特定类型对象的计数
2)TS中的枚举Enum
(1)使⽤枚举,可以定义⼀些带名字的常量。⽤于清晰地表达意图或创建⼀组有区别的⽤例
(2)语法:
enum 枚举名称{成员1,成员2....};
(3)类型
- 字符串
enum b{
red='miaozai',
yellow='yuanzi',
blue='xiaobai'
}
console.log(b.red);
console.log(b.yellow);
console.log(b.blue);
- 数字枚举
enum b{
//默认情况下,第一个枚举值为0,后续依次自增
male=0,
female
};
console.log(b.male);
console.log(b.female);
5.TS中联合类型声明
1)声明
在ts中我们可以使⽤ "|" 进⾏联合类型声明
2)语法
let a:number|string;
a=1;
a='miaozai';
//限制b的范围,b只能是12 13 14 15 如果是其它的报错
let b:12|13|14|15;
b=12;
b=15;
// b=16;//报错
6.TS中类型别名
1)背景
let k= 1|2|3|4|5|;
//我这⾥还有⼀个值p的范围和k是⼀样的
let p=1|2|3|4|5|;
//假如他有⼏⼗个呢?我们这样写岂不是麻烦。
2)语法
type 类型别名的名字(小驼峰命名)=string;
3)代码实现
type myType=1|2|3|4|5|6|7|8|9|10;
let a:myType;
let b:myType;
a=1;
b=2;
console.log(typeof a);
console.log(typeof b);
四、详解TS中的配置
1)开启⾃动编译ts⽂件的功能
(1)⾃动编译单个⽂件
编译⽂件时,使⽤-w指令后,TS编译器会⾃动监视⽂件的变化,并在⽂件发⽣改变时对⽂件进⾏重新编译。
tsc ⽂件名.ts -w
(2)⾃动编译当前项⽬下的所有的ts⽂件
tsconfig.json是⼀个json⽂件,添加配置后,只需要tsc命令即可完成对整个项⽬的编译
2)详解tsconfig.json中的配置选项
- include
定义希望被编译⽂件所有的⽬录
默认值:[**/**]
**:表示任意⽬录
*:表示任意⽂件
//就代表所有的dev⽬录和prop⽬录下的⽂件都会被编译
- exclude
定义不需要编译的⽬录
默认值:["node_modules","bower_components","jspm_packages"]
//代表不编译prop⽬录下的所有⽂件
- extends
定义被继承的配置⽂件
//表示当前配置⽂件会⾃动包含config⽬录下base.json中的所有配置信息
- files
指定需要编译⽂件的列表
案例:
"files":["a.ts","b.ts","c.ts","d.ts"]
// 表示列表中⽂件都会被TS编译器编译
- complierOptions
ts编译时的配置
代码案例:
tsconfig.json文件
{
"include": ["./dev"],//定义被编译文件所在的目录
"exclude": ["./prop/**/*"]//定义不需要编译的目录
"extends": "./config/base"//定义被继承的文件
"files": ["./dev/devDemo.ts"]//指定需要编译的目录
"complierOptions":{}//ts编译时的配置
}
3)complierOptions⼦选项中的配置
- target
指定ts编译的js⽬标版本
可选值:"ES3"(默认), "ES5", "ES6"/ "ES2015", "ES2016", "ES2017"或 "ESNext"。
- module
指定使⽤的模块化规范:
可选值:
"None", "CommonJS", "AMD", "System", "UMD", "ES6"或 "ES2015"。
▶ 只有 "AMD"和 "System"能和 --outFile⼀起使⽤。
▶ "ES6"和 "ES2015"可使⽤在⽬标输出为 "ES5"或更低的情况下。
- lib
指定编译过程中需要引⼊的库⽂件的列表
可选值:
▶ ES5
▶ ES6
▶ ES2015
▶ ES7
▶ ES2016
▶ ES2017
▶ ES2018
▶ ESNext
▶ DOM
▶ DOM.Iterable
▶ WebWorker
▶ ScriptHost
▶ ES2015.Core
▶ ES2015.Collection
▶ ES2015.Generator
▶ ES2015.Iterable
▶ ES2015.Promise
▶ ES2015.Proxy
▶ ES2015.Reflect
▶ ES2015.Symbol
▶ ES2015.Symbol.WellKnown
▶ ES2016.Array.Include
▶ ES2017.object
▶ ES2017.Intl
▶ ES2017.SharedMemory
▶ ES2017.String
▶ ES2017.TypedArrays
▶ ES2018.Intl
▶ ES2018.Promise
▶ ES2018.RegExp
▶ ESNext.AsyncIterable
▶ ESNext.Array
▶ ESNext.Intl
▶ ESNext.Symbol
案例:"lib":["ES6","DOM"]
注意:如果--lib没有指定默认注⼊的库的列表。默认注⼊的库为:
▶ 针对于--target ES5:DOM,ES5,ScriptHost
▶ 针对于--target ES6:DOM,ES6,DOM.Iterable,ScriptHost
- outDir
⽤来指定编译后⽂件所在的⽬录
案例:"outDir":'./指定编译后⽂件所在的⽬录'
- outFile
⽤来指定编译后⽂件所在的⽬录
案例:"outFile":'./dist/main.js'
//就表示把编译后的⽂件合并的main.js这⽂件中,最后只会输出⼀个js⽂件
出现问题的解决⽅案:
mudule只能使⽤ amd或者是system
- allowJs
是否对js⽂件进⾏编译
默认值:false
案例:"allowJs":true
- checkJs
是否检查js代码符合语法规范
默认值:false
案例:"checkJs":true
- removeComments
在编译的时候是否移除注释
默认值:false
- noEmit
不⽣成编译后的⽂件
默认值为:false
案例:"noEmit":true
//这个有什么⽤呢,可能有时候你就想⽤ts的语法检测。就可以使⽤这个
- noEmitOnError
当存在语法错误的时不⽣成编译后的⽂件
默认值:false
案例:"noEmitOnError":true
- alwaysStrict
js中有⼀种模式叫做严格默认,它语法更严格,在浏览器执⾏的性能更⾼,开发时候我们都会让我们的代码在严格模式下执⾏
如果是在js⽂件的话 只需要在js⽂件的开头加⼊⼀个字符串
"use strict";
//就表示开启了js的严格模式
设置编译后的⽂件是否使⽤严格模式
默认值:false
案例:"alwaysStrict":true
当代码中有引⼊导出模块代码时,js会⾃动进⼊严格模式下
- noImplicitAny
不允许隐式的any类型
默认值为:false
案例:"noImplicitAny":true
- strictNullChecks
严格的检查空值
默认值为:false
案例:"strictNullChecks":false
- strict
所有严格检查的总开关
默认值为:false
案例:"strict":false
代码案例:
//这里只是大概演示学习,实际项目实际解决
"compilerOptions": {
"target": "ES6",//指定ts编译的js版本为es6
"module": "amd",//指定使用模块化规范为amd
"lib": ["DOM"]//指定编译过程中需要被引入的为DOM
"outDir": "./dist",//指定编译后文件所在目录
"outFile": "./dist/app.js",//将编译的代码合并成一个文件
"allowJs": true,//允许对js文件编译
"checkJs": true//检查js代码是否符合规范
"removeComments": true,//编译时移除注释
"noEmit": true,//生成编译后的文件
"noEmitOnError": true,//存在语法错误不生成编译后的文件
"alwaysStrict":true//编译后文件使用严格模式
"noImplicitAny":true,//允许隐式any类型
"strictNullChecks":false//严格的检查空值
"strict": true//打开所有严格检查的总开关
}
五、TypeScript语法进阶
1.配置在直接运⾏ts环境
1)⾸先全局安装ts-node
npm install -g ts-node
2)执⾏ts-node命令即可
ts-node 文件名.ts
2.⾯向对象
要创建⼀个对象,必须先定义⼀个类,所谓的类可以理解为对象模型,程序中可以根据类创建指定类型的对象
3.TS中的类
1)、通过class定义类:
//通过class这个关键字来定义⼀个类
class Person{
name:string;
age:number;
}
//通过这个类实例化⼀个对象
//let man=new Person();//括号里面没有参数的时候,括号可以省略的
let man=new Person;
man.name="喵崽";
man.age=2;
console.log(man);
2、静态修饰符(static)
class Animal{
static readonly nameD='猫';//被readonly修饰的属性,只能读取不能修改
say(){
console.log("动物在叫");
}
}
let dog=new Animal;
// dog.name="狗"; //name被static修饰之后只属于class了,不属于实例对象了
// console.log(dog.name);
dog.say();
// Animal.nameD='狗';
// console.log(Animal.nameD);
4.类中的构造⽅法
如何在类中定义⼀个构造⽅法 (1)定义一个无参的构造函数
class Person{
name:string="小白";
constructor(){
console.log(this.name);
}
}
let man=new Person;
(2)定义一个有参的构造函数
class Animal{
name:string;
constructor(name:string){
this.name=name;//this改造
}
}
let dog=new Animal("狗");
console.log(dog.name);
this改造构造⽅法
5.继承——extends
1).什么是继承
继承父类的所有属性和方法
2)代码实现
class Animal{
name:string;
sleep(){
console.log(this.name+"在睡觉");
}
constructor(name:string){
this.name=name;
}
}
class Dog extends Animal{
hobbit(){
console.log(this.name+"爱吃骨头");
}
}
let jm=new Dog("金毛");
jm.hobbit();
console.log(jm.name);
6.TS中的重写
1).什么是重写
⼦类覆盖了⽗类中的属性或者是⽅法叫做重写
2)代码实现
class Animal{
name:string="动物";
sleep(){
console.log("动物在睡觉");
}
}
class Dog extends Animal{
name:string="狗";
sleep(){
console.log("狗在睡觉");
}
}
let jm=new Dog();
console.log(jm.name);
jm.sleep();
7.super关键字
1)super
在当前类中 super就表示当前类的⽗类
2)代码实现
class Animal{
name:string;
sleep(){
console.log(this.name+"在睡觉!!!!");
}
constructor(name:string){
this.name=name;
}
}
class Dog extends Animal{
age:number;
sleep(){
//super 代表父类 父类也被称为超类
super.sleep();
}
constructor(name:string,age:number){
super(name);//要写上,否则会覆盖父类的关键字
this.age=age;
}
}
8.抽象类——abstract
1)什么是抽象类
- 以abstract开头的类被称为抽象类
- 抽象类和其他类区别不⼤,只是不能⽤来创建对象
- 抽象类就是专⻔⽤来被继承的类
2)抽象⽅法
- 抽象类中可以添加抽象⽅法
- 抽象⽅法使⽤abstract开头,没有⽅法体
- 抽象⽅法只能定义在抽象类中,⼦类必须对抽象⽅法进⾏重写
- abstract syaHello():void;
- 抽象类使⽤场景,当你不希望某个类被创建时,可以使⽤抽象类
3)代码实现
abstract class Animal{
name:string="抽象类";
abstract sleep():void;
// 抽象方法特点
// 1:没有方法体,没有实现
}
// let dog=new Animal();//1:抽象类只能被继承,不能初始化一个实例
//2:子类继承抽象类,必须重写抽象类中所有抽象方法,
// class Dog extends Animal{
// sleep(){}
// }
//如果不重写,那么这个类也应该是一个抽想类
abstract class Dog extends Animal{
abstract bark():void;
}
class JM extends Dog{
sleep(){}
bark(){};
}
9.TS中的接⼝——interface
1)什么是接⼝
接⼝⽤来定义⼀个类结构,⽤来定义⼀个类中应该包含哪些属性和⽅法。同时接⼝也可以当成类型声明去使⽤。接⼝可以重复声明,取所有相同接⼝名的并集
2)语法
通过 interface 关键字来定义
3)代码实现
interface myInterface{
name:string;
sleep():void;//1在接口中的方法都是一个抽象的方法,只有方法名,参数,返回类型,没有返回体的实现
}
interface myInterface{//2.接口可重复声明
age:number;
}
let b:myInterface;
b={
name:'xdclass',
age:12,
sleep(){
console.log("myInterface");
}
}
console.log(b.name);
b.sleep();
class Person implements myInterface{//3.通过一个类来实现接口
name:string;//必须包含前面定义的
age:number;
gender:string;
sleep(){
console.log(this.name);
}
constructor(name:string,age:number,gender){
this.name=name;
this.age=age;
this.gender=gender;
}
}
let c=new Person('小D',12,'男');
console.log(c);
c.sleep();
10.TS中属性的封装
1)属性修饰符
- public:修饰的属性可以在任意位置访问(修改),是默认值
- private: 私有属性,私有属性只能在类内部进⾏访问(修改)
-通过在类中添加⽅法使得私有属性可以被外部访问
- protected: 受保护的属性,只能在当前类何当前类的之类中访问(修改)
2)属性封装
class Person {
private name:string;
private age:number;
constructor(name:string,age:number){
this.name=name;
this.age=age;
}
}
3)属性存取器
属性存取器:
- getter ⽅法⽤来读取属性
- setter ⽅法⽤来设置属性
class Person {
private name:string;
private age:number;
constructor(name:string,age:number){
this.name=name;
this.age=age;
}
//读取name
get name():string{
return this.name;
}
//设置neme
set name(name:string):void{
this.name=name;
}
}
11.TS中的泛型
1)、泛型
(1)背景
// 需求 如果我实参可能是 boolean string object 这时该如何解决呢?
function cache(value:number):number{
return value;
}
//解决⽅案1: 指定 形参的类型和返回值类型为any
//⼜出现的问题,如果使⽤any,则会关闭ts的类型检测。不仅影响⾃⼰,还会在赋值时关闭其他变量的类型检测
//使⽤泛型即可解决以上的需求
(2)泛型——不确定的类型
(3)使用场景
在定义函数或类时,如果遇到类型不明确就可以使⽤泛型
2)、语法
function cache<泛型名>(value:泛型名):泛型名{
return 泛型名;
}
3)、泛型的约束
我们可以指定泛型的类型,如果我们不指定TS解析器会根据我们传递的参数进⾏类型推测(建议我们在使⽤ 泛型的时候指定泛型的类型)
使⽤⽅法:
cache<指定泛型类型>('miaozai');
4)、创建多个泛型
//定义
function cache<泛型名1,泛型名2>(value1:泛型名1,value2:泛型名2):T{
return T;
}
//调⽤
cache<泛型类型1,泛型类型2>(实参1,实参2);
5)、泛型在接⼝中的使⽤
interface Animal<T>{
name:T;
}
class Dog<T,F> implements Animal<T>{
name:T;
age:F;
constructor(name:T,age:F){
this.name=name;
this.age=age;
}
}
let cat=new Cat<string,number>("miao",2);//此时T为String类型 age为int类型
console.log(cat);