为vue3学点typescript, 什么是泛型?

往期

第一课, 体验typescriptvue

第二课, 基础类型和入门高级类型git

第三课, 什么是泛型?程序员

第四课, 解读高级类型github

第五课, 什么是命名空间(namespace)?面试

插一课

原本打算接着上节课, 把高级类型都讲完, 可是写着写着我发现高级类型中, 有不少地方都须要泛型的知识, 那么先插一节泛型.typescript

什么是"类型变量"和"泛型"

变量的概念咱们都知道, 能够表示任意数据, 类型变量也同样, 能够表示任意类型:segmentfault

// 在函数名后面用"<>"声明一个类型变量
function convert<T>(input:T):T{
    return input;
}

convert中参数咱们标记为类型T, 返回值也标记为T, 从而表示了: 函数的输入和输出的类型一致. 这样使用了"类型变量"的函数叫作泛型函数, 那有"泛型类"吗?数组

注意: T是我随便定义的, 就和变量同样, 名字你能够随便起, 只是建议都是大写字母,好比U / RESULT.app

泛型类
class Person<U> {
    who: U;
    
    constructor(who: U) {
        this.who = who;
    }

    say(code:U): string {
        return this.who + ' :i am ' + code;
    }
}

在类名后面经过"<>"声明一个类型变量U, 类的方法和属性均可以用这个U, 接下来咱们使用下泛型类:函数

let a =  new Person<string>('詹姆斯邦德');
a.say(007) // 错误, 会提示参数应该是个string
a.say('007') // 正确

咱们传入了类型变量(string),告诉ts这个类的Ustring类型, 经过Person的定义, 咱们知道say方法的参数也是string类型, 因此a.say(007)会报错, 由于007是number. 多以咱们能够经过传入类型变量来约束泛型.

自动推断类型变量的类型

其实咱们也能够不指定类型变量为string, 由于ts能够根据实例化时传入的参数的类型推断出Ustring类型:

let a =  new Person('詹姆斯邦德');
// 等价 let a =  new Person<string>('詹姆斯邦德');
a.say(007) // 错误, 会提示参数应该是个string
a.say('007') // 正确
泛型方法

其实方法和函数的定义方式同样:

class ABC{
    // 输入T[], 返回T
    getFirst<T>(data:T[]):T{
        return data[0];
    }
}
泛型是什么?

说实话ts的文档我找了好几遍, 也没看到他给泛型正式作定义, 只是表达他是一种描述多种类型(类型范围)的格式, 我以为有点抽象, 我用本身的理解具象下: 用动态的类型(类型变量)描述函数和类的方式.

泛型类型

咱们能够用类型变量去描述一个类型(类型范围), ts的数组类型Array自己就是一个泛型类型, 他须要传递具体的类型才能变的精准:

let arr : Array<number>;
arr = ['123']; // 错误, 提示数组中只能够有number类型
arr = [123];

下面咱们本身定义一个泛型类型, 就对开头的convert函数定义:

function convert<T>(input:T):T{
    return input;
}

// 定义泛型类型
interface Convert {
    <T>(input:T):T
}

// 验证下
let convert2:Convert = convert // 正确不报错

泛型接口

经过传入不一样的类型参数, 让属性更灵活:

interface Goods<T>{
    id:number;
    title: string;
    size: T;
}

let apple:Goods<string> = {id:1,title: '苹果', size: 'large'};
let shoes:Goods<number> = {id:1,title: '苹果', size: 43};

扩展类型变量(泛型约束)

function echo<T>(input: T): T {
    console.log(input.name); // 报错, T上不肯定是否由name属性
    return input;
}

前面说过T能够表明任意类型, 但对应的都是基础类型, 因此当咱们操做input.name的时候就须要标记input上有name属性, 这样就至关于咱们缩小了类型变量的范围, 对泛型进行了约束:

// 如今T是个有name属性的类型
function echo<T>(input: T extends {name:string}): T {
    console.log(input.name); // 正确
    return input;
}

一个泛型的应用, 工厂函数

function create<T,U>(O: {new(): T|U; }): T|U {
    return new O();
}

主要想说3个知识点:

  1. 能够定义多个类型变量.
  2. 类型变量和普通类型用法一直, 也支持联合类型/交叉类型等类型.
  3. 若是一个数据是能够实例化的, 咱们能够用{new(): any}表示.

不要乱用泛型

泛型主要是为了约束, 或者说缩小类型范围, 若是不能约束功能, 就表明不须要用泛型:

function convert<T>(input:T[]):number{
    return input.length;
}

这样用泛型就没有什么意义了, 和any类型没有什么区别.

总结

泛型是编译型语言最重要的特性, 泛型写的好就会让人以为代码很高级, 能够说泛型是一个成手ts程序员必须熟练的技巧, 面试的时候是加分项, 因此你们写代码多多用泛型练习哦, 加油ヾ(◍°∇°◍)ノ゙,下面是的用ts写的几个小项目,写的很差, 就是有份热情, 抛砖引玉, 你们确定能写出更好的:

手势库: https://github.com/any86/any-...

命令式调用vue组件: https://github.com/any86/vue-...

工做中经常使用的一些代码片断: https://github.com/any86/usef...

一个mini的事件管理器: https://github.com/any86/any-...