关于TypeScript

TypeScript是JavaScrip 的一个超集,主要提供了类型系统和对 ES6 的支持,它由Microsoft开发,代码开源于 GitHub 上。

它的第一个版本发布于 2012 年 10 月,经历了多次更新后,现在已成为前端社区中不可忽视的力量,不仅在 Microsoft 内部得到广泛运用,而且Angular自angular2起也使用TypeScript作为开发语言。

TypeScript环境准备

安装TypeScript需要npm,如何安装node.js及npm请参考node.js安装教程

TypeScript 的命令行工具安装方法如下:

     npm i -g typescript	

Mac电脑需要添加sudo,否则会无权限安装。

安装完成后,我们就可以使用tsc命令了。

编译一个TS文件可以通过如下命令:

tsc typescript.ts

使用TypeScript编写的文件应该以TS结尾。

基础数据类型

TypeScript常见的原始数据类型有boolean,string,number,null,undefined。

boolean(布尔类型)

需要区分的是boolean和Boolean,boolean是基础的数据类型,其值仅可能为true或者false;而Boolean则为构造函数或者说是对象。也可以通过Boolean来创建布尔值。

let isBoolean:boolean=true;
isBoolean=false;
isBoolean=Boolean(1); 
isBoolean=new Boolean(1);//错误,不能将类型“Boolean”分配给类型“boolean”。“boolean”是基元,但“Boolean”是包装器对象。
let a:Boolean=new Boolean(1);

string(字符串类型)

let s:string='asda';
let s1=`这是一个模板字符串的示例,我可以取变量${s},我还可以执行其他的计算操作等,比如${new Date()}`
console.log(s1);

image.png 如上代码中,可用 ` 来定义ES6中的模板字符串,在模板字符串中可用 $ 的方式来嵌入表达式。

number(数值类型)

let num:number=666;
num=3.1415;
num=NaN;

可用number来定义数值类型,其中,NaN意味着不是数字,但他的类型是number,且NaN与任何对象包括他自己比较是否相等,结果都为false,可用ES6的新方法Number.isNaN()来判断是否为number数值。

null和undefined

在TypeScript中,可以分别用null和undefined来定义对应的数据类型。

let nullObject=null;
let undefinedObject=undefined;

null和undefined是所有类型的子类型,也就是说null和undefined可以赋值给任意类型的变量。

void(空值)

JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void 表示没有任何返回值的函数。

void类型的变量只能赋值为null或者undefined。void类型的变量不能赋值给number类型。

any(任意值)

any用来定义非固定类型的变量,其可以赋值为任意类型的值。在任意值上访问任意属性和方法都是可以的。可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值,且如果变量在声明的时候,未指定其类型,那么它会被识别为任意值类型

let anyThing: any = 'hello';
let something;
console.log(anyThing.myName);
console.log(anyThing.myName.firstName);
anyThing.setName('Jerry');
anyThing.setName('Jerry').sayHello();
anyThing.myName.setFirstName('Cat');
console.log(something.myName);
console.log(something.myName.firstName);
something.setName('Jerry');
something.setName('Jerry').sayHello();
something.myName.setFirstName('Cat');

类型推论与类型检查

如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。

let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
//  Type 'number' is not assignable to type 'string'.

如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查,所以当我们不确定变量的类型或变量的类型可能会有变化时,要么变量不赋初值,要么显式指定变量为any类型。

联合类型

变量的类型可以多种类型中的一种。联合类型使用 | 来分隔每个类型。当TypeScript不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法;在对联合类型的变量赋值时,会根据类型推论的规则推断出一个类型。

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
function getString(something: string | number): string {
    return something.toString();
}
function getLength(something: string | number): number {
    return something.length;//类型“string | number”上不存在属性“length”。类型“number”上不存在属性“length”。
}
myFavoriteNumber = 'seven';
console.log(myFavoriteNumber.length);
myFavoriteNumber = 7;
console.log(myFavoriteNumber.length); // 类型“number”上不存在属性“length”。

接口类型

接口可用来定对象的类型,不仅可用与对类的一部分行为(成员方法)进行抽象外,还可以对对象的属性(成员变量)进行描述。当我们定义一个变量的类型为接口类型时,变量的属性必须与接口保持一致

interface Person {
    name: string;
    age: number;
}
let tom: Person = {
    name: 'Tom'
};//Property 'age' is missing in type '{ name: string; }' but required in type 'Person'.
let tom: Person = {
    name: 'Tom',
    age: 25,
    gender: 'male'
};//不能将类型“{ name: string; age: number; gender: string; }”分配给类型“Person”。对象文字可以只指定已知属性,并且“gender”不在类型“Person”中.

当我们并不要求变量完全匹配接口的属性时,我们可以定义可选属性。可选属性通过 ? 声明,其代表着属性可以不存在。

interface Person {
    name: string;
    age?: number;
}

let tom: Person = {
    name: 'Tom'
};

如上代码中,age被定义为了可选属性,但此时依旧不可以添加未在接口中定义的属性

数组类型

在TypeScript常用来定义数组的方式有两种,无论使用那种方式来定义数组,数组内的数据都必须与定义数组时的类型一致,除非定义时使用任意类型(any)。

类型[]

最简单的定义方式就是使用 类型[] 来定。

let array:number[]=[1,'2'];//不能将类型“string”分配给类型“number”。
array=[1,2];
array.push('8');//类型"8"的参数不能赋给类型“number”的参数。

Array<类型>(数组泛型)

通过Array来定义数组也是常用的方式之一。

let array:Array<number>=[1,2]

函数

一个函数有输入和输出,且函数只接受与声明时数量及类型匹配的参数,即输入多余的(或者少于要求的)参数,是不被允许的

function test(s:string,b:number): number{
    return b;
}

test('1',8);
test('1',8,2);//应有 2 个参数,但获得 3 个
test(8,'1');//类型“8”的参数不能赋给类型“string”的参数

虽然输入多余的(或者少于要求的)参数,是不被允许的,但我们可以通过 来定义可选参数,必须注意的是,可选参数只能在必需参数之后,换句话说,可选参数后面不允许再出现必需参数了

function buildName(firstName?: string, lastName: string) {
    if (firstName) {
        return firstName + ' ' + lastName;
    } else {
        return lastName;
    }
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName(undefined, 'Tom');

// index.ts(1,40): error TS1016: A required parameter cannot follow an optional parameter.

我们也可以在定义函数时给参数设置默认值,设置了默认值的参数会被自动识别为可选参数,此时,虽然在定义函数不再受可选参数后不允许再跟必需参数的限制,但若是在设置了默认值的参数后面跟了必需参数,则设置了默认值的参数也会变相成为必需参数

function test(s:string,b:number=2,ac:string): number{
    return b;
}
test('1',2);//An argument for 'ac' was not provided.
test('1',2,'3');
function test2(s:string,b:number,ac:string='2'): number{
    return b;
}
test2('2',3);

面向对象编程中,类是一类事物的抽象,包含它的属性和行为(方法)。

在TypeScript中,我们通过class关键字来定义类,使用constructor来定义构造方法,在生成新实例的时候,会自动调用构造函数。

class Animal {
    constructor(name) {
        this.name = name;
    }
    sayHi() {
        return `My name is ${this.name}`;
    }
}

let a = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack

与其他面向对象编程语言如JAVA类似,TypeSCript的类也可以通过extends关键字集成类、通过implements关键字实现接口,通过super关键字调用父类的构造函数和方法。必须注意的是,只能单继承,可以多实现

interface parentInterface1{
    parentInterface1():void;
}
interface parentInterface2{
    parentInterface2(name:string):string;
}
class parentClass{
    name1:string;
    constructor(name:string){
        this.name1=name;
    }
    parentClass(){
        console.log(this.name1);
    }
}
class child extends parentClass implements parentInterface1,parentInterface2{
    constructor(name){
        super(name);
    }
    parentInterface1():void{
        super.parentClass();
    };
    parentInterface2(name:string):string{
        return super.name1;
    }
}

TypeScript 可以使用三种访问修饰符,分别是 public、private 和 protected。

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
  • private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
  • protected 修饰的属性或方法是受保护的,它和 private 类似,但它在子类中也是允许被访问的
class parentClass{
    private privateName:string;
    private protectedName:string;
    public publicName:string;
    constructor(name:string){
        this.privateName=name;
        this.protectedName=name;
        this.publicName=name;
    }
    protected getProtectedName(){
        console.log(this.protectedName);
    }
    public getPublicName(){
        console.log(this.publicName);
    }
    private getPrivateName(){
        console.log(this.privateName);
    }
}
class child extends parentClass {
    constructor(name){
        super(name);
        super.getProtectedName();
        super.getPublicName();
        super.getPrivateName();//属性“getPrivateName”为私有属性,只能在类“parentClass”中访问
    }
}

TypeScript更多相关教程地址: TypeScript入门教程 TypeScript中文手册