\

In the cone of light, all is fate

TypeScript语法速成

前言

对于没有接触过JS以外语言的读者,在学习TS前,需要知道以下几点

  • TS是JS的超集,它本身不是一种独立的语言

  • TS的作用是规范代码,限制因JS灵活的特性而写出一些吊炸天的代码

  • 学习TS要结合编译后的JS一起理解,一些看上去很奇妙的代码,编译后平平无奇

  • TS多用于大型项目,必然结合框架使用,只学TS是毫无意义的

 

相关命令

npm install -g typescript 安装

tsc xxx.ts 会编译出名为 xxx.js的文件

node xxx.js 运行

 

变量声明

我们知道相比JS,TS在声明变量时多了类型,不过与其他强类型语言不同,TS不是新的语言,它只是一种规范和超集,所以不会失去JS原有的特性,比如全局变量和局部变量,所以varlet仍然是在的,声明的时候只是多了个:type,比如以下几种类型的变量声明:

var a : string = "string";
var b : number = 0;
var c : any = 0; //any代表任意类型
var d = 0;  //不写就默认是any
​
a = 1;  //error: a是string类型,不能赋值为number
b = 1;  //正确,b本来就是number类型
c = "string";   //error:c虽然是any类型,但一旦赋值就确定了类型,首次赋值是个number,以后都是number
d = "string";   //同理

从以上可以看出,TS的类型声明都是在变量名后加:类型,如果赋值的类型和变量的类型不一样,编译就会报错,注意是编译报错,JS本身是允许我们随意赋值的,请再回顾一下前言中的内容,相信您会有更好的理解。

以上代码在JS里也就长这样:

var a = "string";
var b = 0;
var c = 0; 
var d = 0;  
​
a = 1;  
b = 1;  
c = "string";   
d = "string";   

 

联合类型&类型推断

我们已经知道了any类型的存在,它允许我们在首次为其赋值时,值可以是任意类型。那么思考一下,既然any那么方便,而且声明时可以省略不写,项目中全部类型都用any不就好了?

  • 可以,但又不完全可以,因为这样做的话,使用TS还有什么意义呢?TS本来就是对JS的一种规范用法,最终是编译成JS的,那还不如直接用JS开发。

既然这样,为什么要有any这个类型?

  • 因为我可以不用,但不能没有。而且确实有些场景会需要一个变量,接受任意类型的值,当然这种情况是越少越好的,在能明确知道一个变量会接受什么类型的值时,尽量不要用any。

假如我们知道一个变量只接受特定几种类型,比如number和string时,使用any显得很浪费,我们可以使用联合类型,所谓联合类型,其实就是为一个变量多设置几个类型关键词,当然这不代表这个变量是多种类型的,只是放宽了规范而已。

//TS
var c : number | boolean;
c = 1;
c = true;
//JS
var c;
c = 1;
c = true;

从上面可以看到,无论变量c是什么类型,编译成JS后,就只是个var c,所以说TS只是一种规范,确保在开发时候不会用到过多的隐式转换,将大量的bug扼杀在开发阶段。

顺便讲一下类型推断,所谓类型推断其实就是TS对于未声明类型的一种判断机制,看下面代码很快就能理解:

var e ; //e的类型是any
e = 0;
e = "a";
​
var f = 0;  //f的类型是number
f = "str";  //error

 

类型断言

类型断言可以理解为类型转换,当一个变量A的类型不确定,但又得赋值给另一个变量B时,得保证A的类型和B是相同的,不过TS中的类型断言并不强大,在c#中,类型断言可以作为if的条件,而且是可以隐式转换的,TS中似乎杜绝了隐式转换,但又不是不能做到,所以这个功能在我看来有点奇妙,可以转换但又不能直接转换,断言的类型不对就会报错,那类型对了我还断言干嘛?可能是我还没接触到使用场景。

var c : number | boolean;
var d ;
​
d = c as number;
​
c = 1;
d = c as number;
​
c = true;
d = c as number;    //error
​
var a : number = 0;
var x : string = a as unknown as string;    //先转换成unknown再转换成string
var y : string = <string> <any> a;  //泛型写法,原理同上
var z : string = a as any as string;    //先转换成any再转换成string

 

函数

function greet():string { // 有返回值就把返回类型写在()后
    return "Hello World" 
} 
 
function caller(str:string) { //参数
    console.log(str) 
} 

 

数组

var arr : number = [1,2,3,4];
var arr1 : number = [1,2,3,"4"];    //error
var arr2 : any = [1,"2",true];
var arr3 = [1,"2",true];
//TS中有种叫元组的,其实就是JS的数组,以上arr2、arr3都是

 

接口

接口是TS中的部分,JS中没有接口,但最后还是会编译成JS的代码,TS的接口像C#中的抽象类,又有点像结构体,和数组配合可以将索引设为数字或字符串,这点又像Lua中的表,但本质上来说,就是JS中的对象,JS的对象本来就很灵活。

接口的作用是为数据提供一个模板,数据格式严格按模板来,而接口自身不做实现:

interface A {   //接口A
    firstName:string, 
    lastName:string, 
    sayHi: ()=>string 
} 
 
var B:A = { 
    firstName:"Tom",
    lastName:"Hanks", 
    sayHi: ():string =>{return "Hi there"} 
}
​
var C:A = { 
    firstName:"Jim",
    lastName:"Blakes", 
    sayHi: ():string =>{return "Hello!!!"} 
} 

同样的,接口中的类型也可以是多样的:

interface RunOptions { 
    program:string; 
    commandline:string[]|string|(()=>string); 
} //commandline可以是一个string类型的数组,也可以是string,也可以是函数

接口和数组可以设计关联数组:

//example1
interface A { 
   [index:number]:string 
} 
 
var list1:A = ["John","Bran"] 
​
//example2
interface B { 
   [index:string]:number 
} 
 
var list2:B; 
agelist["John"] = 15   // 正确 
agelist[2] = "nine"   // 错误

接口B可以继承自接口A,用extends关键字继承:

interface A { 
   age:number 
} 
 
interface B extends A { 
   instrument:string 
} 
 
var drummer = <B>{}; //泛型写法,看个人喜好
drummer.age = 27 
drummer.instrument = "Drums" 
​
interface C extends E,F,G{  //继承多个接口
    //...
}

 

这其实就是ES6的内容,简单过一下,如果您对类不熟悉,先记住以下三点类的组成部分:

  • 字段 − 字段是类里面声明的变量。字段表示对象的有关数据。

  • 构造函数 − 类实例化时调用,可以为类的对象分配内存。

  • 方法 − 方法为对象要执行的操作。

class Car { 
    // 字段 
    engine:string; 
 
    // 构造函数,只要创建了类的实例,就会自动执行。可以不写,构造函数是默认存在的
    constructor(engine:string) { 
        this.engine = engine 
    }  
 
    // 方法 
    disp():void { 
        console.log("发动机为 :   "+this.engine) 
    } 
}

编绎成JS后:

var Car = /** @class */ (function () {
    // 构造函数 
    function Car(engine) {
        this.engine = engine;
    }
    // 方法 
    Car.prototype.disp = function () {
        console.log("发动机为 :   " + this.engine);
    };
    return Car;
}());

顺便复习一下为什么方法要写在prototype中,拿上面的代码举例,因为在new一个Car对象时,会为engine开辟一块内存,再new一个Car,会再开一块内存,而protoype中的内容不会重复开辟内存空间,方法一般是可重用的,所以为了节约空间,会写在protoype中。

var a = new Car("a");
var b = new Car("B");
//a.engine !== b.engine
//a.disp === b.disp

这个prototype的特性其实和Static很像,不过TS中的Static并不是用protoype实现的,先说一下Static是什么。Static是类中静态成员的关键字,给静态成员赋值不需要实例化对象,直接类名.成员名即可,这就意义着它像全局变量。

class StaticMem {  
   static num:number; 
   
   static disp():void { 
      console.log(StaticMem.num) 
   } 
} 
 
StaticMem.num = 12     // 初始化静态变量
StaticMem.disp()       // 调用静态方法
​
//编译成JS
var StaticMem = /** @class */ (function () {
    function StaticMem() {
    }
    StaticMem.disp = function () {
        console.log(StaticMem.num);
    };
    return StaticMem;
}());
StaticMem.num = 12; // 初始化静态变量
StaticMem.disp(); // 调用静态方法

访问修饰符是用于设定类中成员、方法可访问程度的关键字

  • public(默认) : 公有,可以在任何地方被访问。

  • protected : 受保护,可以被其自身以及其子类和父类访问。

  • private : 私有,只能被其定义所在的类访问。

class A{
    private a:number = 0;
    b:string;
}
​
var c = new A();
c.a //error:不可访问
c.b //可以访问

类之间可以继承,但只能单继承,子类默认拥有父类的成员和方法,如果父类中有一个A方法,子类中也写了一个A方法,也就是方法名称相同,那么这就叫重写;如果子类中写super.A()则是调用父类中的A方法。

class Parent { 
   A():void {
     
   } 
} 
 
class Child extends Parent { 
   A():void { 
      super.A() // 调用父类的函数
      //...自己的逻辑
   } 
}

 

命名空间

命名空间主要是为了处理变量名重复的问题,如下代码,看一眼应该就理解了:

namespace SomeNameSpaceName { 
   export interface ISomeInterfaceName {      }  
   export class SomeClassName {      }  
}

现在有一种情况,几个不同的文件,用着同一个命名空间,而且会用到其他文件相同命名空间下的变量,该怎么做?用<reference path = "A.ts" />引用:

//A.ts
namespace Drawing { 
    export interface IShape { 
        draw(); 
    }
}
//B.ts
​
/// <reference path = "A.ts" /> 
namespace Drawing { 
    export Circle extends IShape { 
       
    }
}
//run.ts
​
/// <reference path = "A.ts" /> 
/// <reference path = "B.ts" /> 
​
//用到什么就引用什么

命名空间还可以嵌套,不过咱还是别整这些花里胡哨的,了解即可。

文章中还有一些没有讲到的内容,比如对象,循环之类 ,这些都是JS中差不多的东西,此文主要是方便迅速熟悉TS,由于只是讲语法相关内容,所以声明文件没有在文中讲述,因为我认这部分最好是在项目中去熟悉,以后可能会专门写一个TS项目的开发过程。

posted @ 2021-11-29 17:33  Ymrt  阅读(133)  评论(0编辑  收藏  举报