Typescript学习之泛型的理解

Typescript学习之泛型的理解

泛型 官网介绍

软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 
组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,
这在创建大型系统时为你提供了十分灵活的功能。

个人理解的泛型

 泛型是指广泛的类型,多种类型或者任意类型,是一个类型参数或类型变量。

下面是一些泛型案例和理解,以及一些其他概念

泛型初探 为什么需要泛型以及泛型的演变由来
当一个类型变量或类型参数不确定时,希望适用于多个类型,但又不希望定义为any。
下面定义了一个getData函数,T是一个类型变量,参数data和返回值都属于这种类型变量。
function getData<T>(data:T): T {
  return data
}
再比如说,下面定义一个求和函数,参数x和y是数字类型,返回值也是数字类型
function funSums(x: number, y: number): number {
  return x + y
}
假设现在需要一个方法,传入一个数组,返回一个数组
interface Arr extends Array {
  [index: number]: {
    name: string
    serialnumber: number
    data: string
  }
}
let arr: Arr = [
  { name: '嘻嘻嘻', serialnumber: '3', data: '' },
  { name: '嘿嘿嘿', serialnumber: '4', data: '' },
  { name: '哇哇哇', serialnumber: '2', data: '' },
  { name: '哈哈哈', serialnumber: '1', data: '' },
]
function getNewArray(list: Arr): Arr {
  list.sort((a, b) => {
    return a.serialnumber > b.serialnumber ? 1 : -1
  })
  return list
}
let arrList = getNewArray(arr)
上述代码用Arr接口定义了一个对象数组,并用arr实现了这个接口,getNewArray函数接收一个对象数组,经过排序后返回该数组,只改变顺序,不改变对象结构。如果希望传入的是任意类型的数组,经过排序,返回该数组,下面来实现一下。
interface Arr extends Array {
  [index: number]: {
    name: string
    serialnumber: number
    data: string
  }
}
let arr: Arr = [
  { name: '嘻嘻嘻', serialnumber: '3', data: '' },
  { name: '嘿嘿嘿', serialnumber: '4', data: '' },
  { name: '哇哇哇', serialnumber: '2', data: '' },
  { name: '哈哈哈', serialnumber: '1', data: '' },
]
// 第一种写法
function getNewArray<U>(list: U[]): U[] {
  list.sort((a, b) => {
    return a.serialnumber > b.serialnumber ? 1 : -1
  })
  return list
}
// 第二种写法
//function getNewArray<U>(list: Array<U>): Array<U> {
// list.sort((a, b) => {
//    return a.serialnumber > b.serialnumber ? 1 : -1
//  })
//  return list
}
let arrList = getNewArray<Arr>(arr)
上述代码用一个U来做类型变量,使得这个函数可以接收任意类型的参数,并返回处理后的参数。
定义了泛型函数后,可以用两种方法使用。
// 定义一个泛型函数
function identity<T>(arg: T): T {
    return arg;
}
第一种是,传入所有的参数,包含类型参数:
let output = identity<string>("myString");
第二种方法更普遍。利用了类型推论 – 即编译器会根据传入的参数自动地帮助我们确定T的类型:
let output = identity("myString"); 
官网案例
function loggingIdentity<T>(arg: T[]): T[] {
    console.log(arg.length);  
    return arg;
}
笔记
如上所述,你可以这样理解loggingIdentity的类型:泛型函数loggingIdentity,
接收类型参数T和参数arg,它是个元素类型是T的数组,并返回元素类型是T的数组。
如果我们传入数字数组,将返回一个数字数组,因为此时 T的的类型为number。
这可以让我们把泛型变量T当做类型的一部分使用,而不是整个类型,增加了灵活性。
使用带有调用签名的对象字面量来定义泛型函数,类型参数实参和形参命名可以不一致,位置和数量一致就可以。
function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: {<U>(arg: U): U} = identity;
泛型接口
// 定义一个函数参数接口
interface GenericIdentityFn {
    <A>(arg: A): A;
}

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

let myIdentity: GenericIdentityFn = identity;
我们可能想把泛型参数当作整个接口的一个参数。 这样我们就能清楚的知道使用的具体是哪个泛型类型(比如: GenericIdentityFn 而不只是GenericIdentityFn )。 这样接口里的其它成员也能知道这个参数的类型了。
// 定义一个函数参数接口
interface GenericIdentityFn<A> {
    (arg: A): A;
}

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

let myIdentity: GenericIdentityFn<number> = identity;
泛型类 泛型类看上去与泛型接口差不多。 泛型类使用( <>)括起泛型类型,跟在类名后面。
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
泛型约束
下面定义了一个Lengthwise 接口,约定了一个number类型的length属性,loggingIdentity函数的类型参数T继承了这个接口,如果参数没有length属性就会报错,这个就是泛型约束。
interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  
    return arg;
}
在泛型约束中使用类型参数
function getProperty(obj: T, key: K) {
    return obj[key];
}

let x = { a: 1, b: 2, c: 3, d: 4 };

getProperty(x, "a"); // okay
getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.
上述声明一个类型参数T,且它被另一个类型参数K所约束。 
比如,现在我们想要用属性名从对象里获取这个属性。 
并且我们想要确保这个属性存在于对象 obj上,因此我们需要在这两个类型之间使用约束。
在泛型里使用类类型
function createFunc<T>(Func:{new():T}):T {
  return  new Func()
}
一个更高级的例子,使用原型属性推断并约束构造函数与类实例的关系。
class BeeKeeper {
    hasMask: boolean;
}

class ZooKeeper {
    nametag: string;
}

class Animal {
    numLegs: number;
}

class Bee extends Animal {
    keeper: BeeKeeper; // 指向BeeKeeper类
}

class Lion extends Animal {
    keeper: ZooKeeper; // 指向ZooKeeper类
}
// 泛型类型继承了Animal类,具有了numLegs属性
function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}

createInstance(Lion).keeper.nametag;  // typechecks!
createInstance(Bee).keeper.hasMask;   // typechecks!

结尾

 以上就是关于Typescript的泛型理解和总结,有错误和补充的话,欢迎指正,会修改,谢谢。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值