初识TypeScript
TypeScript是JavaScript类型的超集,简称TS,包含JavaScript语法支持ES6标准,所以在学习TypeScript之前一定要先学会ES6,TypeScript是由微软开发的自由和开源的编程语言,TypeScript支持类型定义、类、接口、枚举、泛型等特性,所以其设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的JavaScript可以运行在任何浏览器上。
TypeScript的特点:
1.编译JavaScript
TypeScript可以编译出纯净、简洁的JavaScript代码,并且可以运行在任何浏览器上,Node.js环境中和任何支持ECMAScript 3(或更高版本)的JavaScript引擎中。
2.类型系统
类型系统(静态类型化)可以在开发人员编写脚本时检测错误,查找并修复错误是当今开发团队的迫切需求。有了这项功能,开发人员可以编写出可读性高,维护性强的代码。
3.开发大型项目
有时为了改进项目,需要对代码库进行小的增量更改。这些小小的变化可能会产生严重的、意想不到的后果,因此有必要撤销这些变化。使用TypeScript工具来进行重构变的容易、快捷。
4.更好的协作
在开发大型项目时,会有许多开发人员,此时会出现各种各样的问题和bug,类型系统检测是一种在编码期间检测错误的功能,而不是在编译项目时检测错误,这为开发团队创建了一个更高效的编码和调试的过程。
数据类型与推断
TypeScript在声明变量或是函数时需要添加约束也叫类型注解,支持的基本数据类型和引用类型有:
string、number、boolean、null、undefined、symbol、array、object、Function、any,
空值可以使用void来表示,void可以表示变量,也可以表示函数返回值。
let hello:string="hello world"; //必须为字符串类型
let message:any=”我是任意类型”;
接口与联合类型
联合类型表示取值可以为多种类型中的一种,可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值。
//联合类型 使用|支持多个类型
let muchtype:string|number="hello";
muchtype=1;
muchtype=true//报错
接口可以理解为一种类型,一个规范,一个约束,可以对数组、对象、类进行约束,接口中可定义可选属性、只读属性、任意属性,这样在开发代码时便于调试。
interface Istate{
name:string;
age:number;
}
let user:Istate={name:"张三",age:10}
接口也支持可选属性,代码示例如下:
interface Istate2{
name:string;
age?:number;//加上问号表示存疑,可有可无
}
let user2:Istate2;
user2={name:"张三",age:20};
user2={name:"李四"}
接口也支持只读属性,代码示例如下:
interface Istate2{
readonly name:string; //readonly只读,name不能更改
}
let user2:Istate2;
user2={name:"李四"}
user2.name="王五";//不能更改,否则报错
接口的属性也支持联合类型,代码示例如下:
interface Istate3{
name:string|number;//可以是联合类型
}
let user3:Istate3={name:"张三"}
user3={name:18}
如果对象属性个数不确定的情况下,可以这样定义,代码示例如下:
interface Istate3{
name:string|number;//可以是联合类型
age:number;
[propName:string]:any;
//[propName1:string]:any;//动态添加属性 propName自定义
}
let user3:Istate3={name:"张三",age:10,gender:"男",money:2000000}
接口也可以约束数组,代码示例如下:
interface IArray{
[index:number]:number;
}
let array:IArray=[1,2,3,1]
数组与元组
数组对象是使用单独的变量名来存储一系列的值。最常见的定义方式有两种,第一种是类型+方括号定义,第二种是数组泛型来定义。
let arr:number []=[1,2,3];
let arr2:string []=["1","2","3"];
let arr3:any []=[1,"2",true];
数组泛型的方式,代码示例如下:
let arrType:Array<number>=[1,2,3];
let arrType2:Array<string>=["1","2","3"];
let arrType3:Array<any>=[1,"2",true];
元组的代码示例:
let arr4:[number,string,boolean]=[1,"大家好",true];
let arr5:[{name:string,age:number}]=[{name:"大家好",age:1}]
函数类型
函数类型的约束支持有函数本身的参数约束和返回值约束,如果想支持联合类型的函数关系可以使用重载的方式。
声明一个参数约束和返回值约束的函数,代码示例如下:
function funcType(name:string,age:number):number{
return age
}
let ageNum:number=funcType("张三",18);
如果函数的参数出现了可选参数可以使用问号来进行约束,代码示例如下:
function funcType(name:string,age:number,gender?:string):string{
return name
}
funcType("张三",18,"男");
funcType("张三",18);
如果函数的参数不确定,可以使用扩展运算符的方式来定义动态参数,代码示例如下:
function dynFuncType(name:string,age:number,...args:any):any{
console.log(args);//结果:[“11”,”22”,33]
return args
}
let dynUser:any=dynFuncType("张三",18,"11","22",33);
函数的参数也可以设置默认值,这一点和ES6是一样的,区别就是要给参数设置一个类型约束
function funcType(name:string="张三",age:number=18):number{
return age;
}
也可以声明表达式类型的函数,代码示例如下:
let funcType:(name:string,age:number)=>number=function(name:string=”张三”,age:number=20):number{
return age
}
也可以使用接口来约束函数,代码示例如下:
interface IFuncType{
(name:string,age:number):number;
}
let funcType:IFuncType=function(name:string,age:number):number{
return age;
}
对于联合类型的函数,可以使用重载的方式实现,代码示例如下:
//重载
function getValue(value:number):number;
function getValue(value:string):string;
//联合类型的函数
function getValue(value:string|number):string|number{
return value;
}
let val:string=getValue("张三");
let val2:number=getValue(18);
如果函数没有返回值可以使用void来定义,代码示例如下:
//void规定函数没有返回值
function fun():void{
let age=20
}
类型断言
类型断言可以用来手动指定一个值的类型,将一个联合类型的变量指定为一个更加具体的类型。
//类型断言
function getAssert(name:string|number){
// return (<string>name).length;//<string>name类型断言,不支持jsx
return (name as string).length;//使用as实现类型断言
}
getAssert("张三");//2
getAssert(30);//undefined
注意:类型断言不是类型转换,断言成一个联合类型不存在的类型是不允许的。
类型别名
类型别名可以用来给一个类型起一个新名字,采用关键字type定义,可以设置字符串和数值类型
//约束数值类型
type strType=string|number|boolean|object;
let str:strType="10";
str=10;
str=true;
str={name:"aa"}
//约束字符串
type gender="男"|"女";
function getGender(s:gender):string{
return s;
}
getGender("男");
枚举
enum Days{
Sun,
Mon,
Tue,
Wed,
Thu,
Fri,
Sat
}
console.log(Days.Sun)//使用枚举名Sun,可以获取到枚举值0
console.log(Days[0])//使用枚举值0,可以获取到枚举名Sun
console.log(Days.Sat)//6
console.log(Days[6])//Sat
console.log(Days);//枚举类型会被编译成一个双向映射的对象
枚举里面的元素默认情况下从0开始为元素的编号,也可以手动的指定成员的编号,代码示例如下:
enum Color{
Red=10,
Green=9,
Blue=1000
}
console.log(Color);
类的修饰符
在TypeScript中类的修饰符有public、private和protected这三个修饰符,接下来看一下这三个修饰符的说明:
public修饰的属性或方法是共有的,可以在任何地方被访问到,默认所有的属性或者方法都public的。
private修饰的属性或方法是私有的,不能在声明它的类外面访问,只能在本类内部访问使用。
protected修饰的属性或者方法是受保护的,它和private类似,唯一的区别是可以在继承的类中访问,比如父类的protected成员成为子类的protected成员,只允许子类成员访问。
class Person{
public name="张三";//公共,默认为public
age=18;
private gender="男";//私有
protected height="175";//受保护的
public say(){
console.log("我叫:"+this.name+",性别:"+this.gender+",身高:"+this.height);
}
}
let p=new Person();
console.log(p.name,p.age);
p.say();
class Child extends Person{
callParent(){
console.log(this.height);
}
}
let c=new Child();
c.callParent();
接口约束类
在面向对象编程中,接口是一种规范的定义,它定义了行为和动作规范,在开发大型项目时通常采用面向对象的编程思路,那么就要用到接口来约束类,实现高内聚低耦合的代码规范。
interface ISuperMan{
name:string;
age:number;
fly:Function;
}
interface IIronMan{
eat:Function
}
interface ISpiderMan{
run:Function
}
interface IMan extends ISuperMan,IIronMan,ISpiderMan{}
class Man implements IMan{
name:string="超人";
age:number=30;
fly(){
console.log(`${this.name}在飞`)
}
eat(){
}
run(){
}
}
let man=new Man();
man.fly();
泛型
泛型是指在定义函数、接口或类的时候,不预先指定具体类型,而在使用的时候在指定类型的一种特性,比如定义一个带有参数的函数,未来在调用这个函数时,传入的值类型不确定,有可能是string,也有可能是number,这时可以使用泛型来解决这样的问题。
function createArray<T>(length: number, value: T): Array<T> {
let arr: T[] = [];
for (let i = 0; i < length; i++) {
arr[i] = value;
}
return arr;
}
let strArray: string[] = createArray<string>(3, "张三");
console.log(strArray);
let numArray: number[] = createArray<number>(10, 1);
console.log(numArray);
一个函数也可以有多个泛型参数
function createMan<T,K>(name:T,age:K):[T,K]{
return [name,age];
}
let result=createMan<string,number>("张三",30);
console.log(result[0],result[1]);//结果:张三 30
泛型接口
在定义接口时,可以为接口中的属性或方法定义泛型类型,在使用接口时,在指定具体的泛型类型
interface ICreate{
<T>(name:string,age:T):string
}
let func:ICreate=function<T>(name:string,age:T):string{
return name+","+age
}
func<number>("李四",20);//结果:李四,20
泛型接口在类中如何使用
//泛型接口
interface IUser<T>{
name:string;
age:number;
getUserInfo:()=>T
}
//定义一个用户类
class User implements IUser<string>{
public name:string;
public age:number;
constructor(name:string,age:number){
this.name=name;
this.age=age;
}
getUserInfo(){
return `姓名${this.name},年龄${this.age}`;
}
}
let user=new User("张三",30);
console.log(user.getUserInfo());//结果:姓名张三,年龄30
泛型类是在定义类时,为类中的属性或方法定义泛型类型,在创建类的实例时,再指定特定的泛型类型。
class Counter<T>{
public num:T;
total(price:T,amount:T){
return Number(price)*Number(amount);//如果需要计算必须转成Number类型
}
}
let ct1=new Counter<number>();
ct1.num=10;
ct1.total(100,ct1.num);
let ct2=new Counter<string>();
ct2.num="100";
ct2.total("200",ct2.num);
泛型约束
泛型约束是指确保泛型类使用的参数是提供特定方法的类型,比如直接对一个泛型参数使用length属性或是用push()方法,会报错,因为这个泛型根本不知道它有这个属性或是这个方法,使用泛型约束可以解决这一的问题。
interface IArrayFunc{
//可以在setArray函数中使用push方法
push:Function;
//可以在setArray函数中使用length属性
length:number;
}
function setArray<T extends IArrayFunc>(data:T,value:string|number){
data.push(value);
console.log(data);//结果为:[1,2,3,'4']
console.log(data.length); //结果为:4
}
setArray([1,2,3],"4");
命名空间
命名空间主要用于组织代码,避免命名冲突,解决重名的问题,在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内,TypeScript的命名空间使用namespace关键字创建,可以将代码包裹起来,如果需要在外部访问命名空间内的接口或是类,则需要在类和接口添加export关键字。
namespace A{
export let message="大家好";
export class Person{
public name:string="张三";
public say(){
console.log("大家好,我是"+this.name);
}
}
}
console.log(A.message);//结果:大家好
let person=new A.Person();
person.name; //结果:张三
person.say();