Type Script --泛型 学习

一. 场景
如何支持多种类型,并且还能保证数据完整性?
思考:
普通函数的样子?
定义参数->返回值。
二. 实现

  1. 泛型:
    在支持多种类型参数的同时,数据的完整性还能得到保证。
  2. 定义:类型变量
    指特殊的一种变量,只表示类型,不提供值。类型变量可以自定义,只要在数量上和使用方式上能对应上就可以。
  3. 最基本的实现方法:
    通过类型变量替换掉参数的类型,<T>是函数签名,用来表示其是一个泛型函数。
function indentity <T>(arg: T){
    return arg;
}

我们有两种方法来使用此函数:
①声明返回值

let output = indentity<string> ("its me!");

② 无返回声明(类型推论)

let output = indentity ("its me!");

利用了类型推论 -- 即编译器会根据传入的参数自动地帮助我们确定T的类型。

  1. 泛型变量
    当参数类型被标记为泛型变量后,那么当在方法体内使用这个参数时,必须把他当成任意类型。你不能在方法体内使用某些特定类型的方法,如使用arg.length(),因为你传递过来的可能是任何值,而数字就没有length方法。
  2. 任意类型的数组
    我们平时使用数组时,通常会声明此数组的类型,如:args : string[] 。此数组只能传递字符串类型。通过学习泛型,如果我们把这个数组改为泛型数组,那么我们就可以传递任意类型的值了。
    现在我们声明为 args : T[] 。
function loggingIdentity <T>(arg: T[]): T[] {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}

我们既可以使用数组的方法,又可以传递任意的值了。

  1. 泛型类型
    思考:我们可以声明泛型数组以外的泛型类型吗?
    首先,我们看一下使用带有调用签名的对象字面量来定义泛型函数的写法:
function identity<T>(arg: T): T {
    return arg;
}
let myIdentity: {<T>(arg: T): T} = identity;

函数的泛型类型表示为约束函数的 参数类型返回值类型。<T>表示函数签名。
这种写法等同于:

function identity<T>(arg: T): T {
    return arg;
}
let myIdentity: <T>(arg: T)=>T = identity;

对象字面量 可以单独拿出来,用来定义一个接口:

interface GenericIdentityFn {
    <T>(arg: T): T;
}

然后把这个接口作为函数的类型:

interface GenericIdentityFn {
    <T>(arg: T): T;
}

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

let myIdentity : GenericIdentityFn = identity;

如果我们把函数签名从接口声明中拿出来,放到接口后面,就像这样:

interface GenericIdentityFn<T>{
    (arg: T): T;
}

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

let myIdentity : GenericIdentityFn<number> = identity;

那么,当我们使用接口声明的时候,就需要同时传入<T>的类型,如上面例子里的number类型。可以根据需要,约束使用时的类型了。
接下来,在接口的基础上,学习如何创建泛型类。

  1. 泛型类
    泛型类跟泛型接口差不多,同样是用<T>放在类名称后面,如
class GenericNumber<T>{
  zeroValue:T ;
  add:(x:T,y:T)=>T;
}

add属性被声明为一个函数名称,并约束了参数类型与返回值类型,但是此时并没有方法体。
当初始化这个类的时候,可以用任意类型,比如使用number类型:

let myGenericNumber=new GenericNumber<number>();
myGenericNumber.zeroValue:T =0;
myGenericNumber.add = (x,y)=>x+y;

alert(myGenericNumber.add(myGenericNumber.zeroValue, 2));  //2

同样,也可以初始化的时候,传入string:

let myGenericNumber=new GenericNumber<number>();
myGenericNumber.zeroValue:T ="1";
myGenericNumber.add = (x,y)=>x+y;

alert(myGenericNumber.add(myGenericNumber.zeroValue, "hello"));  //1hello

需要注意的是,泛型类里不能使用静态属性。原因是静态属性是类在声明时就进行初始化,跟类的实例无关。

  1. 泛型约束
    我们还可以对<T>的内容进行约束,比如T类型里面应该必须包含某一个属性,以方便我们程序的使用。
    还以接口为例子,这时候,我们需要在接口里面声明这个属性。
interface Lengthwise {
    length: number;
}

当使用泛型继承这个接口,那么就要求 实现T类型的类型里,必须包含length这个属性

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}
loggingIdentity(3);  // Error, number doesn't have a .length property

loggingIdentity({length: 10, value: 3});  //10 //right,{length: 10, value: 3}对象 包含 length属性。

另外的约束使用:K类型被T类型约束。本例里K类型必须被包含在T里。

function getProperty(obj: T, key: K) {
    return obj[key];
}

让我们最后看一下这个例子,你会怎么理解它呢?

class BeeKeeper {
    hasMask: boolean;
}

class ZooKeeper {
    nametag: string;
}

class Animal {
    numLegs: number;
}

class Bee extends Animal {
    keeper: BeeKeeper;
}

class Lion extends Animal {
    keeper: ZooKeeper;
}

class Dog{
   numLegs: number;
}

class Bird{
  hasMask: boolean;
  nametag: string;
}

function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}

createInstance(Lion).keeper.nametag;  // typechecks!
createInstance(Bee).keeper.hasMask;   // typechecks!
createInstance(Bee).numLegs;    // typechecks!
createInstance(Dog).numLegs;  // typechecks!
createInstance(Bird).hasMask;   //error typechecks
createInstance(Bird).nametag;  //error typechecks

解释:

因为泛型A继承了Animal的约束,所以调用方法createInstance()时,参数对象必须包含Animal 类的属性 :numLegs;
参数(c: new () => A)会返回一个名为A的对象,但是没有值。方法体通过调用 new c() 这个构造方法生成一个类型为A的对象。
正如:createInstance(Bird).hasMask; //error typechecks
createInstance(Bird).nametag; //error typechecks
会报错:


Argument of type 'typeof Bird' is not assignable to parameter of type 'new () => Animal'.
Type 'Bird' is not assignable to type 'Animal'.
Property 'numLegs' is missing in type 'Bird'.


而Dog因为包含了Animal的numLegs 属性,则类型检测通过。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • object 变量可指向任何类的实例,这让你能够创建可对任何数据类型进程处理的类。然而,这种方法存在几个严重的问题...
    CarlDonitz阅读 1,021评论 0 5
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,703评论 9 118
  • 写在开头:本人打算开始写一个Kotlin系列的教程,一是使自己记忆和理解的更加深刻,二是可以分享给同样想学习Kot...
    胡奚冰阅读 1,550评论 1 3
  • 起因来自以下短信:Hello everyone, thank you for doing such wonderf...
    爱吃土豆的两只牛阅读 334评论 0 1
  • 越来越喜欢能做好小事的人。 越来越喜欢能信守承诺的人。 越来越喜欢胸中有沟壑的人。 一个兄弟来电,工作遇到麻烦,郁...
    大树的森林阅读 300评论 0 0

友情链接更多精彩内容