(1)什么是TypeScript?
TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 (ES6)标准。
TypeScript 由微软开发的自由和开源的编程语言。
TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
(2)JavaScript 与 TypeScript 的区别
TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改。
TypeScript 通过类型注解提供编译时的静态类型检查。
TypeScript 可处理已有的 JavaScript 代码,并只对其中的 TypeScript 代码进行编译。
(3)TypeScript环境搭建
地址: https://nodejs.org/en/download/
node -v
npm config set registry https://registry.npm.taobao.org
配置之后可以验证是否成功:
npm config get registry
npm i -g typescript
tsc -v
Version 5.2.2
(4)编写第一个TS程序
console.log('李祥');
tsc demo.ts
(1)TS中的类型声明
类型声明是TS中⾮常重要的特点。
通过类型声明可以指定TS中变量(形参)的类型。
指定类型后,当变量赋值时,TS编译器就会⾃动检查值是否符合声明类型,如果符合就赋值,不符合就报错。
简单的说,类型声明就是给变量设置了类型。使得变量只能存储某种类型的值。
let 变量名:类型;//只声明,未赋值
let 变量名:类型=值;//声明并赋值
function fun(参数:类型,参数:类型):类型{}
TS的基础数据类型
number类型:双精度64位浮点值。它可以用来表示整数和分数。
boolean类型:表示逻辑值:true和false
string类型:一个字符系列,使用单引号(')或双引号(")来表示字符串类型。反引号(`)来定义多行文本和内嵌表达式
(2)TS自动类型判断
TS拥有⾃动类型判断机制。
当对变量的声明和赋值同时进⾏时,TS编译器会⾃动判断变量类型。
所以如果你的变量的声明和赋值是同时进⾏的,可以省略掉类型声明。
//声明一个变量未赋值
let num:number;
num = 7;
//声明一个变量进行赋值
let sum = 9;
console.log(typeof num);
console.log(typeof sum);
number
number
(3)TS字面量声明
客观存在⽆法改变的值。
//使⽤字⾯量声明后a的值,永远是23 相当于常量不能修改
let a:23;
//如果修改ts就会提醒
const a=23;
(4)TS中的any类型
any
表示任意数据类型,⼀个变量设置类型为any后相当于对该变量关闭了TS的类型检测。
//显式的any类型声明
let a:any;
a=12;
a=true;
a="lixiang";
//隐私的any类型声明,声明如果不指定类型,则TS解析器会⾃动判断变量的类型为any
let b;
b=23;
b=false;
b="lixiang";
使用any类型的变量可以被赋值为任何类型的值,而不会导致编译器报错或警告。使用any类型时需要注意,因为它是一个非常灵活的类型,可能会导致类型不一致或意外的行为。
(5)TS中unknown类型
unknown
是TypeScript中比较特殊的一种类型,它用于描述类型不确定的变量。这与any类型
相似,但更安全,因为对未知值做任何事情都是不合法的。
unknown:表示未知类型的值;
let b:unknown;
b=123;
b=true;
b='lixiang';
let c:unknown;
c='123';
let d:string;
d=c;
//此时TS解析器提示是报错的
//虽然 变量中的字⾯量都是string,但是 d是string类型c是unknown所以不能赋值
//假如我就想让c的值赋值给d,应该这样操作
if(typeof c ==='string'){
d=c;//这样就可以完成赋值了
}
(6)TS中的类型断⾔
类型断⾔:可以⽤来告诉TS解析器变量的实际类型
语法:
利⽤类型断⾔来处理上⼀节的unknown赋值的⼩问题
let c:string;
let d:unknown;
d="lixiang";
c=d as 'string';
console.log(typeof c);
输出结果:string
(1)对函数中形参进⾏类型声明
与JavaScript不同的是TypeScript函数声明中增加了对形参类型的声明
function fun(num1:number,num2:number):number{
return num1+num2;
}
这样声明的函数,参数只能传递两个数值类型的值,返回也是一个数值类型的值。
所以传入一个参数,或者传入的参数不是数字的编译器都会报错。
//传递不是number的实参
fun('li','xiang');
//多参数
fun(123,4556,55,56);
//少参数
fun(11);
(2)函数中返回值类型的声明
function fun(){
return true;
}
//这个时候TS解析器会根据返回值的类型进⾏判断,返回值类型是什么函数返回值类型就是什么
let result=fun();
console.log(typeof result);
打印结果:boolean
//void:⽤来表示空,以函数为例,就表示没有返回值的函数
function fun():void{
return;
}
//never:永远不会返回结果
function fun():never{
throw new Error("异常");
}
(1)对象类型声明
let a:object; //声明一个object对象类型的数据
let b:{name:string};//声明一个对象b,里面有个string类型的name属性
//给b变量赋值
b={name:'lixiang'}
{属性名1:属性值1,属性名2:属性值2}
当我们采用这种方式指定对象的时候,必须要求格式一摸一样。
let b:{name:string,age?:number,sex?:string}; // 在属性后面加个?表示这个属性非必需
b={name:'lixiang'};
b={name:'zhangsan',age:18};
let b:{name:string,[propName:string]:unknown};
//这就表示我们指定b中存储的类型是对象,并且这个对象必须含有⼀个name的属性,类型为string,这个对象还可以有其他可选属性,只要属性名满⾜是字符串,属性值是unknown即可
[propName:string]:这个任意命名,就表示属性的名字,这个属性名字的类型是string。js中属性名⼀般都是⽤string定义。
[propName:string]:unknown;合起来就代表属性名为string类型,为可选的并且这个属性的值为unknown类型。
(2)数组类型声明
第一种:let 变量名:类型名[];
第二种:let 变量名:Array<类型名>;
let e:string[]; //声明一个字符串类型的数组,表示e中只能存储字符串
let d:number[]; //声明一个数字类型的数组
let f:boolean[]; //声明一个boolean类型的数组
let e:Array<number>; //另一种方式声明数值类型的数组
let e:Array<any>; //声明一个存储任意类型的数组
let e:any[]; //声明一个存储人意类型的数组
(3)元组类型声明
元组(tuple)就是定⻓的数组。且类型自定义,类型相互对应。
let h:[string,string];//定义元组存储两个值,第⼀个值是string类型。第⼆个值也是string
//定义的时候多⼀个少⼀个也不⾏,必须按照声明的结构定义数组,不然TS解析器就会提示报错
h=['li','xiang'];
let s:[string,number];//定义元组存储两个值,第⼀个值是string类型。第⼆个值是number
s=['s',1];
元组书写语法:[类型,类型,类型];
(4)枚举类型声明
枚举(Enum),可以定义⼀些带名字的常量。⽤于清晰地表达意图或创建⼀组有区别的⽤例。
enum 枚举名称{成员1,成员2....};
// 默认情况下,第⼀个枚举值是0,后续⾄依次增1
enum Color
{
red,
blue,
yellow
}
alert(Color.blue);
enum Gender {
male = '1',
female = '0',
}
alert(Gender.male);
(5)联合类型声明
在ts中我们可以使⽤ "|" 进⾏联合类型声明
语法:let 变量名:声明类型1|声明类型2.....;
//就表示声明⼀个变量名,它可以是类型1还可以是类型2或者更多
let sex = string:number;
sex = 1;
sex='1'
//这两种赋值方式都可以
(6)类型别名
声明一个类型别名:type t = ‘’;
声明一个a的类型变量:type a = 1|2|3|4|5|;
这时我们声明一个b变量:let b = a; b就等价于let b = 1|2|3|4|5|;
(1)⾃动编译⽂件
编译⽂件时,使⽤-w指令后,TS编译器会⾃动监视⽂件的变化,并在⽂件发⽣改变时对⽂件进⾏重新编译。
tsc ⽂件名.ts -w
如果直接使⽤tsc指令,则可以⾃动将当前项⽬下的所有的ts⽂件编译成js⽂件。
要在项⽬的根⽬录下创建⼀个ts的配置⽂件 tsconfig.json。tsconfig.json是⼀个json⽂件,添加配置后,只需要tsc命令即可完成对整个项⽬的编译。
(2)tsconfig.json中的配置项讲解
默认值:[**/**]
案例:"include":["dev/**/*","prop/**/*"]
**:表示任意⽬录
*:表示任意⽂件
就代表所有的dev⽬录和prop⽬录下的⽂件都会被编译
默认值:["node_modules","bower_components","jspm_packages"]
案例:"exclude":["./prop/**/*"]
代表不编译prop⽬录下的所有⽂件
案例:"extends":"./config/base"
表示当前配置⽂件会⾃动包含config⽬录下base.json中的所有配置信息
案例:
"files":
[
"one.ts",
"two.ts",
"three.ts",
"four.ts"
]
表示列表中⽂件都会被TS编译器编译
target:指定ts编译的js⽬标版本
可选值:"ES3"(默认), "ES5", "ES6"/ "ES2015", "ES2016", "ES2017"或 "ESNext"。
案例:"compilerOptions":{"target":"ES6"}
表示我们所编写的ts代码将会被编译ES6版本的js代码
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"
案例:
"compilerOptions":{
"target":"ES6",
"lib":["ES6","DOM"]
}
注意:如果--lib没有指定默认注⼊的库的列表。默认注⼊的库为:
针对于--target ES5:DOM,ES5,ScriptHost
针对于--target ES6:DOM,ES6,DOM.Iterable,ScriptHost
outDir:⽤来指定编译后⽂件所在的⽬录
案例:
"compilerOptions":{
"target":"ES6",
"lib":["ES6","DOM"],
"outDir":'./dist'
}
outFile:将编译的代码合并成⼀个⽂件
案例:
"compilerOptions":{
"target":"ES6",
"lib":["ES6","DOM"],
"outDir":'./dist',
"outFile":'./dist/main.js'
}
"outFile":'./dist/main.js'
就表示把编译后的⽂件合并的main.js这⽂件中,最后只会输出⼀个js⽂件
mudule只能使⽤ amd或者是system
其实outFile这个功能我们在⼯作中也很少使⽤,因为我们打包的时候都是通过打包⼯具进⾏使⽤的。
allowJs:是否对js⽂件进⾏编译
默认值:false
案例:"allowJs":true
checkJs:是否检查js代码符合语法规范
默认值:false
案例:"checkJs":true
removeComments:在编译的时候是否移除注释
默认值:false
noEmit:不⽣成编译后的⽂件
默认值为:false
案例:"noEmit":true
noEmitOnError:当存在语法错误的时不⽣成编译后的⽂件
默认值:false
案例:"noEmitOnError":true
alwaysStrict:js中有⼀种模式叫做严格默认,它语法更严格,在浏览器执⾏的性能更⾼,开发时候我们都会让我们的代码在严格模式下执⾏。
如果是在js⽂件的话 只需要在js⽂件的开头加⼊⼀个字符串。
"use strict";
设置编译后的⽂件是否使⽤严格模式
默认值:false
案例:"alwaysStrict":true
noImplicitAny:不允许隐式的any类型
默认值为:false
案例:"noImplicitAny":true
strictNullChecks:严格的检查空值
默认值为:false
案例:"strictNullChecks":false
strict:所有严格检查的总开关
默认值为:false
案例:"strict":false
(1)直接运⾏ts⽂件
npm install -g ts-node
ts-node hello.ts
运行结果:hello lixiang
(2)TypeScript中的类
//通过class这个关键字来定义⼀个类
class Person{
name:string;
age:number;
}
//通过这个类实例化⼀个对象
let person=new Person;
如果不传递参数的话()可以省略
被static修饰的属性或者是⽅法,属于类的。可以通过类名调⽤,不属于实例的,实例没办使⽤
class Person {
static name: string = "lixiang";
static sayHello() {
console.log("你好!");
}
}
Person.sayHello();
使⽤⽅法:放在属性名或者⽅法名前⾯
被readonly修饰的属性,只能读取不能修改
案例:Person.full_name='lixiang';
(3)类中的构造⽅法
class Dog{
constructor(){
console.log("我创建⼀个Dog");
}
}
const dog=new Dog();
//当我们调 new Dog();的时候我们就等于调⽤Dog中的构造⽅法
//在实例犯法中,this就表示当前的实例
//在构造⽅法中当前对象及时当前新建的那个对象
//可以通过this向新建的对象中添加属性
class Dog {
constructor(name: string) {
console.log(name);
}
}
const dog = new Dog("黑妞");
//创建的时候必须传递⼀个string类型的参数
class Dog {
name: string;
constructor(name: string) {
this.name = name;
}
}
(4)TS中的继承
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
eat() {
console.log(this.name + "吃东西");
}
}
class Dog extends Animal {
constructor(name: string) {
super(name);
}
}
此时,Animal被称为⽗类,Dog被称为⼦类,使⽤继承后,⼦类将会拥有⽗类所有的⽅法和属性。
通过继承可以将多个类中共有的代码写在⼀个⽗类中。
这样只需要写⼀次即可让所有⼦类都同时拥有⽗类中的属性和⽅法。
如果希望在⼦类中添加⼀些⽗类中没⼜的属性或⽅法直接加就⾏。
(5)TS中的重写
重写父类的方法,子类中会覆盖掉父类的方法
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();
运行结果:
狗在睡觉
狗
(6)super关键字
1):在当前类中 super就表示当前类的父类
2):如果在子类中写了构造方法,在子类构造方法中必须对父类的构造方法进行调用
子类不写构造方法,父类将自动调用,如果子类写构造方法,则会把父类构造方法覆盖调用,所有必须调用父类的构造方法
super();//有参数也需要传递对应的参数,不然会TS解析器会提示报错
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;
}
}
let jm=new Dog("金毛",12);
jm.sleep();
运行结果:
金毛在睡觉
(7)TS中的抽象类
以abstract开头的类被称为抽象类,抽象类和其他类区别不大,只是不能用来创建对象。抽象类就是专门用来被继承的类。
抽象类中可以添加抽象方法
抽象方法使用abstract开头,没有方法体
抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
abstract syaHello():void;
abstract class Animal{
name:string="抽象类";
abstract sleep():void;
}
class JM extends Dog{
sleep(){
console.log("狗在睡觉");
}
}
let jm = new JM();
jm.sleep();
运行结果:
狗在睡觉
(8)TS中的接口
接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法。同时接口也可以当成类型声明去使用。接口可以重复声明,取所有相同接口名的并集
通过 interface 关键字来定义
//interface
interface myInterface{
name:string;
sleep():void;
}
interface myInterface{
age:number;
}
class Person implements myInterface{
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('lixiang',19,'男');
c.sleep();
运行结果:
lixiang睡觉
(9)TS中属性的封装
public:修饰的属性可以在任意位置访问,是默认值
private: 私有属性,私有属性只能在类内部进行访问,通过在类中添加方法使得私有属性可以被外部访问
protected: 受保护的属性,只能在当前类何当前类的之类中访问
class Person {
private name:string;
private age:number;
constructor(name:string,age:number){
this.name=name;
this.age=age;
}
}
getter 方法用来读取属性
setter 方法用来设置属性
class Person {
private name:string;
private age:number;
constructor(name:string,age:number){
this.name=name;
this.age=age;
}
get name():string{
return this.name;
}
set name(name:string):void{
this.name=name;
}
}
let obj =new Person("lixiang",18);
obj.setAge=12;
console.log(obj.getAge);
运行结果:
12
(10)TS中的泛型
在定义函数或类时,如果遇到类型不明确就可以使用泛型
泛型就是一个不确定的类型
function cache<泛型名>(value:泛型名):泛型名{
return 泛型名;
}
我们可以指定泛型的类型,如果我们不指定TS解析器会根据我们传递的参数进行类型推测(建议我们在使用泛型的时候指定泛型的类型)
使用方法:
cache<指定泛型类型>('lixiang');
function cache<泛型名1,泛型名2>(value1:泛型名1,value2:泛型名2):T{
return T;
}
cache<泛型类型1,泛型类型2>(实参1,实参2);
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 jm=new Dog<string,number>("黑妞",12);
console.log(jm.name);
console.log(jm.age);
运行结果:
黑妞
12