「掘金日新计划 · 8 月更文挑战」的第1天——两小时轻松入门TypeScript

135 阅读17分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情 image.png

前言

最近在无意中看到了关于ts+vue3的项目,突然上手感觉很生疏,突然发现一直以来一直在写js,好像把之前的ts忘的差不多了,正好现在重新整理一下,分享给大家,本文比较简单,相对于看起来比较轻松,同时如有不足,欢迎指正。

一、认识TypeScript

1.回顾JavaScript

在正式开始认识TypeScript之前,我们需要回顾一下JavaScriptJavaScript是动态类型语⾔(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 k1|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);