TypeScript学习笔记(三)泛型、模块化和命名空间

时间:2024-01-30 21:27:13

一、泛型

1. 泛型函数

function getMin<T>(arr: T[]):T {
    if(arr.length === 0) {
        throw new Error("输入的数组没有元素"); 
    }
    let res: T = arr[0];
    for (let i = 1; i < arr.length; i++) {
        if(arr[i] < res) {
            res = arr[i];
        }
    }
    return res;
}

C++ 的泛型函数非常类似

2. 泛型类

下面这个类可以求出当前列表中最小的元素

class MinClass<T> {
    private dataArr: T[] = [];
    public add(...val: T[]): void {
        this.dataArr.push(...val);
    }
    public getMin(): T {
        if(this.dataArr.length === 0) {
            throw new Error("当前数组没有元素"); 
        }
        let res: T = this.dataArr[0];
        for (let i = 1; i < this.dataArr.length; i++) {
            if(this.dataArr[i] < res) {
                res = this.dataArr[i];
            }
        }
        return res;
    }
}

3. 泛型接口

写法一

interface ConfigFn {
    <T>(value:T):T;
}
let getData:ConfigFn = function<T>(value:T):T {
    return value;
}
getData<string>("张三");

写法二

interface ConfigFn<T> {
    (value:T): T;
}
function getData<T>(value:T) {
    return value;
}
var myGetData:ConfigFn<string> = getData;
myGetData("20");

两种写法的区别

  • 写法一:一般用于在使用时才规定泛型的类型,用法类似泛型函数
  • 写法二:一般用于约束函数参数传递的类型,类似 Java 中的:
public void func(List<Integer> array){
  ...
}

这样的参数约束。

二、模块化

TypeScript 中一个 ts 文件中的函数和变量都是该文件私有的,只有通过 export 输出才能被外部调用。

1. export写法一

例如以下情况,我们希望暴露两个函数供外部调用,可以这样写:

// db.ts
export function getData():any[] {
    console.log("获取数据库的数据");
    return [
        { title:"121312" },
        { title:"123123" }
    ]
}

export function save() {
    console.log("保存数据成功")
}

在需要使用的地方使用 import 关键字进行引入,语法如下:

import { aaa,bbb } from "模块的路径";

注意,引入时不需要加最后的 .ts 后缀名,示例如下:

// index.ts
import { getData,save } from "./05module";
let data:any[] = getData();
console.log(data);
save();

因为模块的输出默认是以对象形式进行的(即所有export的东西都被封装到一个对象中),所以如果我们希望直接使用其他模块的方法或字段,需要使用 {} 运算符取出来再使用。

2. export写法二

我们也可以将所有的东西统一输出,避免编写多个 export

function getData():any[] {
    console.log("获取数据库的数据");
    return [
        { title:"121312" },
        { title:"123123" }
    ]
}

function save() {
    console.log("保存数据成功")
}

// 将所有需要输出的东西包装成对象
export {
    getData,
    save
}

3. 为引入的方法或变量起别名

当我们觉得引入的方法或变量的名字使用起来不太方便等时可以为它们起别名,使用 as 关键字:

import { save as saveData } from "./05module";
saveData();

4. export default的使用

一个模块中 export default 只能使用一次,一般当只希望暴露一个方法时可以使用,例如:

// db.ts
function getData():any[] {
    console.log("获取数据库的数据");
    return [
        { title:"121312" },
        { title:"123123" }
    ]
}
export default getData;

而此时的引入也不相同:

import getData from "./05module";
let data:any[] = getData();
console.log(data);

在引入的时候我们不需要使用 {} 操作符进行获取了,此外我们 import 后面的变量名也不需要和export的变量相同:

import hello from "./05module";
let data:any[] = hello();
console.log(data);

这样也是可以得到正确结果的

tips:使用 export 来导出一个类的操作和导出变量和方法的操作都是相同的,因为编译成 js 代码后,一个类本质上就是一个构造函数,在 js 中函数就是一种变量类型,因此操作都是一致的。

三、命名空间

语法:

namespace 命名空间名 {
  ...
}

作用:一个模块内可以有多个命名空间,不同命名空间中的相同标识符的变量、函数和类等等可以重名不发生冲突

导出命名空间:

export namespace A {
  ...
}

引入命名空间的方法和引入变量及函数的方式一致。

tips:命名空间中的内容默认都是私有的,即使导出了命名空间,但默认还是无法直接使用到命名空间内部的函数的,需要将它们也export出来。

示例:

export namespace A {
    interface Animal {
        name: string;
        eat():void;
    }
    export class Dog implements Animal {
        name: string;
        constructor(name:string) {
            this.name = name;
        }
        eat(): void {
            console.log(`小狗${this.name}在吃狗粮`);
        }
    }
    export class Cat implements Animal {
        name: string;
        constructor(name:string) {
            this.name = name;
        }
        eat(): void {
            console.log(`小猫${this.name}在吃鱼`);
        }
    }
}
  
let dog = new A.Dog("小黑");
dog.eat();
image-20210810125654550