简介
1.什么是TypeScript
TypeScript是由微软开发的一款开源的编程语言。 TypeScript 是Javascript的超集,遵循最新的ES5、ES6规范。
2.为何选择TypeScript
TypeScript 增加了代码的可读性和可维护性 TypeScript 非常包容 TypeScript 拥有活跃的社区
3.安装TypeScript
npm install -g typescript
或
cnpm install -g typescript
或
yarn global add typescript
5.查看版本
tsc -v
通过查看版本检测typescript是否安装成功
6.运行
步骤一: 写一个index.ts文件,例如:
console.log('hello typescript!');
步骤二:如何运行.ts文件
1)手动运行
在终端运行index.ts文件,命令如下:
tsc hello.ts
2)vscode自动运行
步骤一:生成配置文件tsconfig.json
tsc --init
执行此命令后生成tsconfig.json文件
步骤二:终端 ——> 任务 ——> tsc:构建tsconfig.json)
由于浏览器不支持typescript 和 es6语法,所以通过此将其编译为es5语法。 执行此命名后,生成了一个index.js文件
数据类型
typescript 中为了使编写的代码更规范,更有利于维护,增加了类型校验,主要提供以下类型:
1.布尔类型(boolean)
let flag:boolean = true;
2.数字类型 (number)
var num:number = 10;
3.字符串类型 (string)
let str:string = '字符串';
str = '修改字符串';
str = 123;//报错
4.数组类型 (array)
TypeScript像JavaScript一样可以操作数组元素。 有两种方式可以定义数组。
第一种:在元素类型后面接上[],表示由此类型元素组成的一个数组
let arr:number[] = [1,3,5,7];
第二种:使用数组泛型,Array<元素类型>:
let arr2:Array<number>=[1,2,3,4,5,6];
5.元组类型 (tuple)
属于数组的一种元组
允许表示一个已知元素数量和类型的数组,各元素的类型不必相同,元素的值必须和类型相对应。
let tuple1:[string,number,boolean] = ['ts',3.1415,false];
tuple1 = ['html',9,true];//正确
tuple1 = ['css',false,10];//报错
6.枚举类型 (enum)
是对JavaScript标准数据类型的一个补充。
用于定义数值集合。为一组数值赋予友好的名字。
默认情况下,从0开始为元素编号。 也可手动指定成员的数值。
个人理解:事先考虑到某一变量可能取的值,用含义清楚的单词来表示它的每一个值
比如表示支付状态 pay_status, 0 未支付 1支付 2 交易成功
enum Color {Red, Green, Blue}; //默认值从0 开始,依次计算
let c: Color = Color.Blue;//2
console.log(Color.Red,Color.Green,Color.Blue); //0,1,2
enum pay_status {
unpaid = -1,//未支付
paid = 0,//支付
success = 1 //交易成功
}
let pay_unpaid = pay_status.unpaid;//-1
let pay_paid = pay_status.paid;//0
let pay_success = pay_status.success;//1
7.任意类型 (any)
var num1:any = 123;
num1 = 'hello';
num1= true;
num1 = [1,2,3];
null 和 undefined
其他类型的子类型
var num4:undefined;
console.log(num4);//undefiend
var num5:number;
console.log(num5);//输出:undefiend,但报错
var num6:null;
// num6 = 123;//报错
num6 = null;
// 一个元素可能是number类型 ,可能是null ,可能是unmdefined
var num7:number | undefined | null;
num7 = undefined;
num7 =null;
num7 = 88;
8.void类型
void类型像是与any类型相反,它表示没有任何类型
当一个函数没有返回值时,你通常会见到其返回值类型是 void
function kong():void{
console.log('kong----');
}
kong()
声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null
never类型
函数
1.函数定义
- 函数声明
function fun1(): string { // 返回一个字符串 return 值类型必须与方法类型一致
return "Hello World"
}
let aaa = fun1();
- 匿名函数
let fun2 = function():number{
return 99;
}
fun2();
2.函数传参
- 声明函数
function getInfo(name:string,age:number):string{
return `${name}---${age}岁`
}
console.log(getInfo('张三',20));
- 匿名函数
let niming = function(name:string,age:number):string{
return `${name}有---${age}岁了`
}
console.log(niming('赵四',30));
3.可选参数
es5里面方法的实参和形参可以不一样,但是ts中实参和形参个数必须一样,如果不一样就需要配置可选参数
TypeScript里我们可以在参数名右侧使用 ?来表示可选参数
可选参数必须配置到最后面
function getInfoSel(name:string,age?:number):string{
if(age){
return `${name}----${age}`
}else{
return `${name}----年龄保密`
}
}
console.log(getInfoSel('张三',45)); //张三----45
console.log(getInfoSel('赵四'));//赵四----年龄保密
4.默认参数
es5中没法设置默认参数,在 ES6 中,允许给函数的参数添加默认值,TypeScript 将默认参数识别为可选参数
function getInfoDefault(name:string,age:number=20):string{
if(age){
return `${name}----${age}`
}else{
return `${name}----年龄保密`
}
}
console.log(getInfoDefault('李五'));//李五----20
5.剩余参数
剩余参数会被当做个数不限的可选参数
ES6 中,可以使用 ...rest 的方式接收函数参数的值 (相当于与es5中的arguments)
...rest参数一般放在末尾
function sum1(...result:number[]):number{
var sum = 0;
for(var i=0;i<result.length;i++){
sum+=result[i];
}
return sum;
}
console.log('sum1:',sum1(1,2,3,4));//10
function sum2(a:string,...result:string[]):string{
let sum = a;
for(var i=0;i<result.length;i++){
sum+=result[i];
}
return sum;
}
console.log('sum2:',sum2('hello,,,,','a','b','c','d')); //hello,,,,abcd
6.函数重载
java中,重载指的是2个或2个以上的同名函数,但他们的参数不一样,这时会出现函数重载的情况
typescript中,重载是指通过为同一个函数提供多个函数定义来实现多种功能的目的。
function getMsg(name:string):string;
function getMsg(age:number):number;
function getMsg(str:any):any{
if(typeof str==='string'){
return `我是${str}`
}else{
return `我的年龄是${str}`
}
}
console.log(getMsg('钱六'));//我是钱六
console.log(getMsg(26));//我的年龄是26
7.箭头函数
function getInfodd(name:string,age:number=20):string{
return '12222'
}
// ====>
let getInfodd1 = (name:string,age:number=20): string => {return '12222'}
console.log(getInfodd1('栈'));
class类
1.概述
在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。
class 的本质是 function。
它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
2.类的定义
1) 声明类
class Person{
name:string;
constructor(name:string){ //构造函数 实例化类的时候触发的方法
this.name = name;
}
}
var p = new Person('小二');
console.log('姓名:',p.name); // 姓名:小二
2) 表达式类 命名类表达式
let per = class Person {
name:string
constructor(name:string) {
this.name = name;
}
}
let p1 = new per('李四');
console.log('姓名:',p1.name);//姓名:李四
匿名类表达式
let per = class {
name:string
constructor(name:string) {
this.name = name;
}
}
let p1 = new per('张三');
p1.name; //张三
3.继承 extends
类(子类)从基类(父类)中继承属性和方法,并可以扩展自己的方法
class Person{
name:string;
constructor(name:string){ //构造函数 实例化类的时候触发的方法
this.name = name;
}
run():void{
console.log(`${this.name}在跑步`);
}
}
// web类 继承person类
class Web extends Person{
constructor(name:string){
super(name);//调用父类构造函数
}
work():void{
console.log(`${this.name}在两江新区工作`);
}
}
var w = new Web('李四');
w.run();//李四在跑步
w.work();//李四在两江新区工作
4.类里面的修饰符
修饰符分为public、protected、private 和 readonly
public:公有的
在类里面、子类、类外面都可访问 , 默认值,可省略
/*基类*/
class Person{
public name:string; //属性 前面省略了public关键字
constructor(name:string){ //构造函数 实例化类的时候触发的方法
this.name = name;
}
public getName():string{
return `名字是:${this.name}`; //*** 在类里面可以访问
}
}
var j =new Person('晓晓');
j.name;// *** public类型 在外可以访问
j.getName(); // *** public类型 在外可以访问
/*子类*/
class Child extends Person{
constructor(name:string){
super(name);
}
}
var h = new Child('儿子');
h.name; //*** public类型 在子类可以访问
h.getName() //*** public类型 在子类可以访问
protected:受保护的在类本身里面、子类里面可以访问,在类外部不可访问
/*基类*/
class Person1{
protected name:string; // 保护类型
constructor(name:string){ //构造函数 实例化类的时候触发的方法
this.name = name;
}
protected getName():string{
return `名字是:${this.name}`;
}
}
var j =new Person1('晓晓');
j.name; //报错 早类外部不能访问
j.getName();//报错 在类外部不能访问
/*子类*/
class Child extends Person1{
constructor(name:string){
super(name);
}
}
var h = new Child('儿子');
h.name;//protected 在子类可以访问
h.getName();//protected 在子类可以访问
private:私有的在类本身里面可访问 ,子类和类外部都不可访问
略
5.静态属性和静态方法,实例属性和实例方法
静态属性:通过类名可以直接访问的属性
实例属性:通过new 出来的实例访问的属性
静态方法:通过类名可以直接访问的方法
实例方法:通过new 出来的实例访问的方法
总结:
在class的{}区间内,只能写构造器、静态方法和静态属性、实例方法,实例属性写在constructor(){}中 静态方法 和 静态属性 不需实例化,可直接由类本身调用
class Animal{
constructor(name,age){
//实例属性
this.name = name;
this.age = age;
}
//静态属性
static info = 'eeee';
// 实例方法
jiao(){
console.log('这是动物的实例方法');
}
// 静态方法
static show(){
console.log('这是静态方法');
}
}
const a1 = new Animal('伶邪',28);
console.log(Animal.info);//info是Animal的静态属性
Animal.show();//show是Animal的静态方法
console.log(a1.name);//实例属性
a1.jiao();//jiao是Animal的实例方法
6.抽象类、继承、多态
1) 抽象类
typescript中的抽象类,它是提供其他类继承的基类,不能被实例化
用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类(子类)中实现
abstract抽象方法只能放在抽象类里面
抽象类和抽象方法用来定义标准,派生类(子类)必须实现抽象类的抽象方法 【标准是必须实现的】
abstract class Ani {
name:string;
constructor(name:string){
this.name = name;
}
abstract eat():any;
run(){
console.log('其他方法可以不实现');
}
}
// var a = new Ani();//报错 抽象类不能被实例化
class Dog1 extends Ani{
constructor(name:string){
super(name);
}
// 抽象类的子类必须实现抽象类里面的抽象方法
eat():any{
console.log(this.name+'吃肉ssss');
}
}
var dog1 = new Dog1('虎子');
dog1.eat();
2) 多态
多态:父类定义一个方法不去实现,让继承它的子类去实现,每一个子类有不同的表现
多态属于继承
class Animal{
name:string;
constructor(name:string){
this.name = name;
}
eat(){//具体操作不知道,由子类去实现,这就是多态
console.log('动物吃的方法');
}
}
class Dog extends Animal{
constructor(name:string){
super(name);
}
eat():void{ //重写eat方法
console.log(this.name+'吃肉');
}
}
class Cat extends Animal{
constructor(name:string){
super(name);
}
eat():void{ //重写eat方法
console.log(this.name+'吃鱼');
}
}
接口
TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些
类型命名和为你的代码或第三方代码定义契约。
理解:接口就是定义标准,要求你按这个标准来实现。
接口关键字
interface
属性接口
对json的约束
官方:传入的对象参数实际上会包含很多属性,但是编译器只会检查那些必需的属性是否存在,并且其类型是否匹配。
类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以
推导:
- 检测必须的属性是否存在,其类型是否匹配
- 属性没有顺序只分,只要存在且类型匹配即可
其他: 直接放参数时,参数个数和参数属性必须与类型检测器中的一致
语法
interface 接口名{
属性1:类型,
属性2:类型,
或
可选属性3?:类型,
...
}
2)对批量方法传入的参数进行约束
interface FullName{
firstName:string;
secondName:string;
}
// 要求:printName方法中 必须传入对象 firstName secondName,通过属性接口实现
function printName(name:FullName) {
console.log('printName:'+name.firstName+ '------'+name.secondName)
}
var obj = {
age:20,
firstName:'jack',
secondName:'westom'
}
// 传入的参数必须包含 firstName secondName
printName(obj)
// 直接放参数时,参数个数和参数属性必须与类型检测器中的一致
/*-------- 正确写法-----------*/
printName({
firstName:'张',
secondName:'三'
});
/*---------错误写法-------------------*/
printName({
age:20, //报错
firstName:'张',
secondName:'三'
});
3)可选属性
对可能存在的属性进行预定义
属性名后用?标识
可选属性可传可不传
interface FullNameSel{
firstName:string,
secondName?:string, //可选参数,可传可不传
}
function getName(msg:FullNameSel) {
console.log('接口实现可选属性','----firsname',msg.firstName);
return msg;
}
getName({// 传入参数时,参数顺序可调
secondName:'五',
firstName:'王',
})
getName({// secondName 为可选参数,可不传
firstName:'王',
})
函数类型接口
对方法传入的参数以及返回值进行约束
语法
interface 接口名{
(参数1:类型1,参数2:类型2):返回值类型;
}
interface encrypt{
(key:string,value:string):string;//传入参数 key,value , 返回值是string
}
/**------- 错误写法(属性 或其类型不匹配)--------------- */
// var md6:encrypt = function(key:number,value:string):string{ //key的类型不匹配
// return key+value;
// }
/**------- 正确写法 --------------- */
// md5必须符合加密函数类型接口
var md5:encrypt = function(key:string,value:string):string{
return key+value
}
md5('sssdddddN','123444555566666')
可索引接口
对数组的索引
// UserArr接口要求是一个满足索引为number,值为string的数组
interface UserArr{
[index:number]:string;
}
//_arr是满足UserArr接口的数组
var _arr:UserArr = ['aaa','bbb'];
// var arrUser = [1,2,3];// 报错
console.log(_arr[0]);
2)对对象的索引
//对象索引为字符串,值为字符串
interface UserObj{
[index:string]:string;
}
var _obj:UserObj ={name:'童',age:'20'}
console.log(_obj.name);
类型接口
对类的约束 ,和抽象类有点相似
关键字 implements
实现接口, 接口的方法一般为空的, 必须重写才能使用
interface Anis{
name:string;
eat(str:string):void; // eat:str是string,无返回值
}
class $Dog implements Anis{
name:string;
constructor(name:string){
this.name = name;
}
eat(){
console.log(this.name+'爱吃肉ooooo');
}
}
接口问题
接口继承implements
子接口继承父接口,子接口也需遵循父接口的规则。
interface AnimalS{
eat():void;
}
interface Human extends AnimalS{ //Human接口继承Animals接口
work():void;
}
class Php implements Human{
name:string;
constructor(name:string){
this.name = name;
}
eat(){
console.log(this.name+'喜欢吃快餐');
}
work(){
console.log(this.name+'在长安集团工作');
}
}
let php = new Php('小李');
php.eat();
php.work();
复杂:
interface AnimalS{
eat():void;
}
interface Human extends AnimalS{ //Human接口继承Animals接口
work():void;
}
class Programmer{
name:string;
constructor(name:string){
this.name = name;
}
coding(code:string){
console.log(this.name+ code);
}
}
class Java extends Programmer implements Human{
constructor(name:string){
super(name);
}
eat(){
console.log(this.name+'喜欢吃素食');
}
work(){
console.log(this.name+'是java工程师');
}
}
let fgl = new Java('花花');
fgl.eat();
fgl.work();
fgl.coding('在写java代码并且测试')
泛型
泛型的定义
软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
通俗理解:泛型就是解决 类 接口 方法的复用性,以及对不特定数据类型的支持
T:表示的泛型,T是什么类型,返回就是什么类型
例子说明:
// 只能返回string类型的数据
function getData(value:string):string{
return value;
}
// 同时返回 string类型 和 number类型等 ,但any放弃了类型检查,传入的参数和返回的参数可能不一致
function getData1(value:any):any{
return value;
}
// ***** 优化:
// 泛型 可以支持不特定的数据类型, 要求:传入的参数和返回的参数一致
// T:表示的泛型,T是什么类型,返回就是什么类型
function getData2<T>(value:T):T{
return value;
}
let a1 = getData2<number>(12345);
console.log(a1,typeof(a1));
let a2 = getData2<string>('12345');
console.log(a2,typeof(a2));
泛型变量
将泛型当做变量
function identity<T>(arg:T):T{
console.log(typeof arg);
return arg;
}
let output1=identity<string>('myString');
let output2=identity('myString');
let output3:number=identity<number>(100);
let output4:number=identity(200);
泛型函数
function identity<T>(arg:T):T{
return arg;
}
let myIdentity:{<T>(arg:T):T}=identity;
泛型接口
interface ConfigFn1{
<T>(value:T):T;
}
var getDataS:ConfigFn1=function<T>(value:T):T{
return value;
}
getDataS<string>('aaa');
getDataS<number>(1244555);
改造后:
interface ConfigFn2<T>{
(value:T):T;
}
function getDataS1<T>(value:T):T{
return value;
}
var _getD:ConfigFn2<string> = getDataS1;
_getD('bbbbbbbbb')
泛型类
class Min1<T>{
list:T[] = [];
add(num:T):void{
this.list.push(num);
console.log(this.list);
}
min():T{
var minNum = this.list[0];
for(var i=0;i<this.list.length;i++){
if(this.list[i]<minNum){
minNum = this.list[i];
}
}
return minNum;
}
}
var minB = new Min1<number>();//实例化类
minB.add(11);
minB.add(2);
minB.add(4);
minB.add(88);
console.log
console.log('最小数:',minB.min());
var minC = new Min1<string>();//实例化类
minC.add('zbout');
minC.add('mli');
minC.add('esss');
minC.add('bsss');
console.log
console.log('最小字符串:',minC.min());
模块 与 命名空间
TypeScript 1.5里术语名已经发生了变化。
“内部模块”现在称做“命名空间”。
“外部模块”现在则简称为“模块”,
这是为了与 ECMAScript 2015里的术语保持一致,
(也就是说 module X { 相当于现在推荐的写法 namespace X {)。
模块的概念 (自己理解 ):
1.把一些公关的功能单独抽离出一个文件作为模块。
2.一个文件就是一个模块。
3.模块里面的变量 函数 类等默认是私有的,如果外部要访问模块里的数据,
需要通过export暴露模块里面的数据(变量、函数、类...)
4.暴露后 通过 import 引入模块就可使用模块里的数据
例子:
步骤一:创建模块,在module文件下创建test.ts,将外部要访问的数据通过 export暴露出去
export namespace A{
// 要使用命名空间中的数据,也要暴露出去
export let str:string = '12345'
}
export namespace B{
export let str:string = '12345'
}
步骤二:使用模块,使用前需用import引入模块
import {A,B} from './modules/test'
console.log(A.str);
console.log(B.str);
命名空间:
在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放在命名空间
命名空间的对象通过expoert暴露,才能被访问
namespace A{
// 要使用命名空间中的数据,也要暴露出去
export let str:string = '12345'
}
namespace B{
export let str:string = '12345'
}
console.log(A.str);
console.log(B.str);
命名空间和模块的区别:
命名空间:内部模块,主要用于组织代码,避免命名冲突。
模 块:ts的外部模块的简称,侧重代码的重写,一个模块里可能有多个命名空间