Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

typescript 泛型 #61

Open
zzkkui opened this issue Dec 3, 2020 · 0 comments
Open

typescript 泛型 #61

zzkkui opened this issue Dec 3, 2020 · 0 comments

Comments

@zzkkui
Copy link

zzkkui commented Dec 3, 2020

泛型

设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是

  • 类的实例成员
  • 类的方法
  • 函数参数
  • 函数返回值

在更加一般化的场景,我们的类型可能并不固定已知,它和any有点像,只不过我们希望在any的基础上能够有更近一步的约束。其实就是在定义的时候不指定具体类型,用一个变量替代,使用时在指定类型替换泛型变量

常见:Array<String>

例如:

// T 是一个抽象类型,只有在调用的时候才确定它的值
function reverse<T>(items: T[]): T[] {
    var toreturn = [];
    for (let i = items.length - 1; i >= 0; i--) {
        toreturn.push(items[i]);
    }
    return toreturn;
}

泛型类型

// 泛型函数
function identity<T>(arg: T): T {
  return arg;
}

// 普通接口
interface GenericIdentityFn {
// 泛型函数
  <T>(arg: T): T;
}

// 泛型接口
interface GenericIdentityFn1<T> {
  (arg: T): T;
}

let myIdentity = identity;
let myIdentity1: <T>(arg: T) => T = identity;
let myIdentity2: <U>(arg: U) => U = identity;
// {<T>(arg: T): T} 接口
let myIdentity3: { <T>(arg: T): T } = identity;
let myIdentity4: GenericIdentityFn = identity;
// 将泛型参数当作接口的参数
let myIdentity5: GenericIdentityFn1<number> = identity;

泛型类

// 泛型类
class GenericNumber<T> {
// 断言
//    zeroValue!: T;
//    add!: (x: T, y: T) => T;
    zeroValue: T | undefined;
    add: (x: T, y: T) => T = (x: any, y: any) => x + y;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

与接口一样,直接把泛型类型放在类后面,可以帮助我们确认类的所有属性都在使用相同的类型。

类有两部分:静态部分和实例部分。 泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型。

泛型约束

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}

现在这个泛型函数被定义了约束,因此它不再是适用于任意类型。我们需要传入符合约束类型的值,必须包含必须的属性(length),如果不约束,arg.length 会报错

在泛型里使用类类型

class ZooKeeper {
    nametag!: string;
}

class Animal {
    numLegs!: number;
}

class Lion extends Animal {
    keeper: ZooKeeper = new ZooKeeper();
}

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

createInstance(Lion).keeper.nametag; 

综合例子

// 创建一个泛型类
class Queue<T> {
  private data: T[] = [];
  push = (item: T) => this.data.push(item);
  pop = (): T | undefined => this.data.shift();
}

// 简单的使用
// 泛型变量需要外部指定
const queue = new Queue<number>();
queue.push(0);
queue.push('1'); // Error:不能推入一个 `string`,只有 number 类型被允许

function reverse<T>(items: T[]): T[] {
  const toreturn = [];
  for (let i = items.length - 1; i >= 0; i--) {
    toreturn.push(items[i]);
  }
  return toreturn;
}

const sample = [1, 2, 3];
// 这里泛型是指定了 number 类型
let reversed = reverse(sample);

reversed[0] = '1'; // Error
reversed = ['1', '2']; // Error

reversed[0] = 1; // ok
reversed = [1, 2]; // ok

泛型是对类型进行编程

区别于平时我们对值进行编程,泛型是对类型进行编程。

例如常见类型转换:Partial

type Required<T> = { [P in keyof T]-?: T[P] };

这里其实就是接收一个类型,经过处理后返回一个新的类型。

// 这里 <T, U> 可以看作是形参,后面使用类型就是使用这个形参
function ids<T, U>(arg1: T, arg2: U): [T, U] {
  return [arg1, arg2];
}

类型推导与默认参数

泛型也支持类型推导和默认参数

function id<T>(arg: T): T {
  return arg;
}
id<string>("lucifer"); // 完整的写法
id("lucifer"); // 基于类型推导的简写
// 泛型变量 T 默认参数 string
type A<T = string> = Array<T>;
const aa: A = [1]; // type 'number' is not assignable to type 'string'.
const bb: A = ["1"]; // ok
const cc: A<number> = [1]; // ok

什么时候用泛型

  • 根据类型生成新的类型,如 Partial
  • 典型的 id 函数
function id<T>(arg: T): T {
  return arg;
}

进阶

  • 泛型支持函数嵌套
// 类似函数一样
type CutTail<Tuple extends any[]> = Reverse<CutHead<Reverse<Tuple>>>;
  • 泛型支持递归
type ListNode<T> = {
  data: T;
  next: ListNode<T> | null;
}

// 官方 HTMLElement 的定义
declare var HTMLElement: {
    prototype: HTMLElement;
    new(): HTMLElement;
}

参考链接:
https://lucifer.ren/blog/2020/06/16/ts-generics/
https://jkchao.github.io/typescript-book-chinese/typings/generices.html#%E5%8A%A8%E6%9C%BA%E5%92%8C%E7%A4%BA%E4%BE%8B

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant