TypeScript入门(二)

202 阅读6分钟

class 类(属性,方法)

public:公共的;protected:受保护的;private:私有的。

class Person {
    public name: string; //public相当于公开的在外部是可以访问的;可以在当前类,子类,外部使用;
    protected gender: string = '男'; //protected被保护的,当前类,和继承的子类可以使用;也可以设置默认值‘男’
    private age: number = 27; //赋值默认值27;private是私有的,只能在当前的Person类使用;

    constructor(name: string, /*gender: string,*/ public username: string) {
        this.name = name;
        this.username = username;
        /*this.gender = gender;*/
    }
    printAge(age: number) {
        this.age = age;
        console.log(this.age);
        this.setGender(this.gender);//setGender加上private ,可以在内部使用
    }
    /*private*/ setGender(gender: string) { //加上private 属性“setGender”为私有属性,只能在类“Person”中访问。
        this.gender = gender;
        console.log(gender);
    }
}

const person = new Person('yaobinggt', /*'男',*/ 'yaouu');

console.log(person.name, /*person.gender,*/ person.username);//gender是protected(受保护的,无法在外部使用)

person.printAge(32);
person.setGender("女")

类的继承

父类 ↓↓↓

class Person {
    public name: string;
    protected gender: string = '男';
    private age: number = 27; 

    constructor(name: string,public username: string) {//每个类都会自动触发的构造函数(constructor)
        this.name = name;
        this.username = username;
    }
    printAge(age: number) {
        this.age = age;
        console.log(this.age);
    }
    private setGender(gender: string) { //加上private 属性“setGender”为私有属性,只能在类“Person”中访问。
        this.gender = gender;
    }
    printName(name: string) {
        this.name = name;
        console.log(this.name);
    }
}

子类 ↓↓↓

student类继承于(extends) person类;子类继承(extends) 父类可以获得父类所有的内容,但是不能使用父类中私有(private) 的东西

class Student extends Person { 
    public studentId: number;
    constructor(name: string, username: string, studentId: number) {
        super(name, username);
        //console.log(this.gender);//男
        // console.log(this.age);//属性“age”为私有属性,只能在类“Person”中访问。
        this.studentId = studentId;
        // this.setGender('女'); //父类方法是是私有的
    }
    studentPrint() {
        console.log(this.studentId);
    }
    printName(name: string) {//重写父类方法
        this.name = name;
        console.log(this.name);
    }
}

const yaobinggt = new Student('姚冰', '姚UU', 20200810);
//console.log(yaobinggt.name, yaobinggt.username,);//姚冰 姚UU
console.log(yaobinggt);//Student {username: "姚UU", gender: "男", age: 27, name: "姚冰", studentId: 20200810}
yaobinggt.studentPrint();//20200810
yaobinggt.printAge(2);//可以调用父级方法

set get修饰词

用于隔离私有属性和可公开属性

const l = console.log;
class Person {
    private _name: string = 'yaobinggt';//通常定义一个私有(private)属性,会加一个下划线;
    //私有属性赋值(set)
    set setName(val: string) {
        this._name = val;
    }
    //私有属性取值(get)
    get getName() {
        return this._name;
    }
}
//实例化一个person对象
let person = new Person();
l(person.getName);//yaobinggt
person.setName = 'yaouu';//赋值 yaouu
l(person.getName);//取值: yaouu

static 静态属性和方法

//class 静态属性和方法
const l = console.log;
class Person {
    static PI: number = 3.14;//静态属性
    static calcCircle(val: number): number {//静态方法
        return this.PI * val;
    }
}
l(Person.calcCircle(8));//25.12

namespace 命名空间

可以用来隔离我们环境变量的污染;在一个大的项目中我们可能起很多变量,有时候就会出现变量冲突,命名空间就能解决变量冲突的问题,同时命名空间可以一个大的TS文件,把一部分内容抽离到另外一个里面去;

要让外部调用就要加上export

const l = console.log;
namespace MyMath {
    export const l = console.log;
    export const PI: number = 3.14;

    export function sumValue(num1: number, num2: number): number { //
        return num1 + num2;
    }

    export function calcCircle(val: number) {
        return PI * val;
    }
}

const PI: number = 2.66;

MyMath.l(MyMath.sumValue(20, 10));//30
l(PI);//2.66
MyMath.l(PI);//2.66
MyMath.l(MyMath.PI);//3.14

命名空间拆分

首先以命名空间代码为例拆分为三个文件

calcCircle.ts

namespace MyMath {
    const PI: number = 3.14;
    export function calcCircle(val: number) {
        return PI * val;
    }
}

sumValue.ts

namespace MyMath {
    export function sumValue(num1: number, num2: number): number {
        return num1 + num2;
    }
}

app.ts

const l = console.log;
l(MyMath.calcCircle(8));//25.12
l(MyMath.sumValue(5, 15));//20

然后到了最关键的一步,打开终端输入:

tsc --outfile app.js calcCircle.ts sumValue.ts app.ts

tsc:我们要执行的命令 --outfile:表示我们要输出的一个文件名字(所以这里输出的是app.js) 输出文件后面是要要把那些ts文件打包成app.js

多重命名空间

在calcCircle.ts代码内再添加一个命名空间,对外引用同样也要加上export

namespace MyMath {
    //多重命名空间
    export namespace Circle {
        const PI: number = 3.14;
        export function calcCircle(val: number) {
            return PI * val;
        }
    }
}

app.ts如何引用:

const l = console.log;
l(MyMath.Circle.calcCircle(8));//25.12

命名空间引入

/// 在ts文件中表示要引用

/// <reference path="calcCircle.ts" />
/// <reference path="sumValue.ts" />
const l = console.log;
l(MyMath.Circle.calcCircle(8));//25.12
l(MyMath.sumValue(5, 15));//20

然后引入之后,只对app.ts进行编译就可以了

tsc app.ts --outFile app.js

模块module的使用

calcCircle.ts

export const PI: number = 3.14; //export 对外公开
export function calcCircle(val: number) {
    return PI * val;
}

app.ts

const l = console.log;
//模块
import { PI, calcCircle } from './stuff/calcCircle'
l(PI);

有了app.ts,我首先要进行编译

  • 命令行输入 tsc,然后编译成app.js:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var l = console.log;
var calcCircle_1 = require("./stuff/calcCircle");
l(calcCircle_1.PI);
//报错:exports is not defined
  • 命令行输入 tsc --module commonjs app.ts,然后编译成app.js:
"use strict";
exports.__esModule = true;
var l = console.log;
var calcCircle_1 = require("./stuff/calcCircle");
l(calcCircle_1.PI);
//报错:exports is not defined
  • 命令行输入 tsc --module amd app.ts,然后编译成app.js:
define(["require", "exports", "./stuff/calcCircle"], function (require, exports, calcCircle_1) {
    "use strict";
    exports.__esModule = true;
    var l = console.log;
    l(calcCircle_1.PI);
});
//报错: define is not defined

综上不管是commonjs、amd还是其他解析都无法将ts解析成浏览器识别的js;所以我们需要一个systemjs(2.0.0一下的版本)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TypeScript</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/systemjs/0.21.5/system.js"></script>
</head>
<body>
    <script>
        System.config({
            baseUrl: "/",
            packages: {
                "/": {
                    "defaultExtension": "js"//让你的js可以被你的浏览器解读
                }
            }
        })
        System.import("app.js");
    </script>
</body>
</html>

app.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var l = console.log;
//模块
var calcCircle_1 = require("./stuff/calcCircle");
l(calcCircle_1.PI);//3.14
l(calcCircle(32));//100.48

app.ts我们还可以换一种方法引入模块

sumValue.ts

export default function sumValue(num1: number, num2: number): number {
    return num1 + num2;
}
const l = console.log;
//模块
// import { PI, calcCircle } from './stuff/calcCircle';
import * as Circl from './stuff/calcCircle';//另外一种引入方法
import sum from './stuff/sumValue';//另外一种引入方法
l(Circl.PI);//3.14
l(Circl.calcCircle(32));//100.48
l(sum(10, 6));//16

interface 接口

接口其实是在面向对象语言中是一个非常重要的概念,JS不是面向对象语言所以并没有接口这么一个概念,但是再TS中就有接口这么一个概念;可以通过接口把我们的所有属性,它的类型进行一个定义,它在某种意义上跟类型的别名很相识

//interfacr接口
interface Person {
    name: string;
    age: number;//单纯的冒号,必须要写的
    sex?: string;//?: 可选的
    readonly salary: number;//readonly只读不能修改
    [propName: string]: any//任何名字可以赋任何值
    greet(): void;//可以定义一个方法,没有返回值
}

type Person2 = {
    name: string,
    age: number
}
//interface可以继承,type不能继承


let person: Person = {
    name: "yaobinggt",
    age: 28,
    sex: "box",
    salary: 3000,
    greet() {
        console.log("I love litter UU")
    }
}

//调用方法
person.greet();//I love litter UU

function printPerson(person: Person) {
    console.log(`我叫${person.name},今年${person.age}了,性别${person.age},工资一个月${person.salary}`)
}
printPerson(person)

interface接口继承

//interfacr接口
interface PersonInterface {
    name: string;
    age: number;//单纯的冒号,必须要写的
    sex?: string;//?: 可选的
    readonly salary: number;//readonly只读不能修改
    [propName: string]: any//任何名字可以赋任何值
    greet(): void;//可以定义一个方法,没有返回值
}
//interface接口继承
interface Employee extends PersonInterface {
    work: string;
}

const employee: Employee = {
    name: 'yaobinggt',
    age: 32,
    salary: 6000,
    sex: 'boy',
    work: '前端',
    greet() {
        console.log('hello world')
    }
}

console.log(employee);
employee.greet();

泛型(Generic)

在函数中使用泛型;让TS推断类型

function identify<T>(arg: T): T {
    return arg;
}
const l = console.log;
l(identify<string>('string'));//可以明确指定类型

在接口中使用泛型:可以明确指定类型;也可以交给ts推断类型。

const l = console.log;
interface GenericIdentify {
    <T>(age: T): T;
}

function identify<T>(arg: T): T {
    return arg;
}

let myIdentify: GenericIdentify = identify;

//可以明确指定类型:
l(myIdentify<string>("yao-string"));//yao-string

//交给ts推断类型
l(myIdentify(30));//30

泛型还可以在接口中提升;不在函数里面定义;而是在接口上定义。

const l = console.log;
interface GenericIdentify<T> {
    (age: T): T;
}

function identify<T>(arg: T): T {
    return arg;
}

let myIdentify: GenericIdentify<number | string> = identify;//泛型在接口中提升以后要添加类型参数

//可以明确指定类型:
l(myIdentify("yao-string"));//yao-string

//交给ts推断类型
l(myIdentify(30));//30

为泛型添加约束

const l = console.log;
function getLength<T extends { length: any }>(obj: T): any {//给泛型制定了一个约束(extends),传递的参数一定要有length
    return obj.length;
}

const obj = {
    name: 'yaobinggt',
    age: 28,
    length: 12
}
l(getLength(obj));//12
const l = console.log;
function getLength<T extends number>(obj: T): any {//还可以让当前的泛型指定一个类型
    return obj;
}

const obj = 28;//指定了类型,就要传对应的类型

l(getLength(obj));//28

泛型Generic类的应用

普通的类

const l = console.log;
class CountNumber {
    number1: any;
    number2: any;

    constructor(num1: number, num2: number) {
        this.number1 = num1;
        this.number2 = num2;
    }

    calcalate(): number {
        return this.number1 + this.number2;
    }
}

const constNumber = new CountNumber(10, 12)
l(constNumber.calcalate());//22

添加泛型的类

const l = console.log;
class CountNumber<T> {
    number1: T;
    number2: T;

    constructor(num1: T, num2: T) {
        this.number1 = num1;
        this.number2 = num2;
    }

    calcalate(): number {
        return +this.number1 + +this.number2;
    }
}

const constNumber = new CountNumber<string>("10", "12")
l(constNumber.calcalate());//120

还可以对泛型进行一个约束

const l = console.log;
class CountNumber<T extends number> {//对泛型约束为number
    number1: T;
    number2: T;

    constructor(num1: T, num2: T) {
        this.number1 = num1;
        this.number2 = num2;
    }

    calcalate(): number {
        return +this.number1 + +this.number2;
    }
}

const constNumber = new CountNumber<number>(10, 12)//对应的也要是number
l(constNumber.calcalate());//22