typescript 的学习之旅-泛型

泛型

泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

举个例子,比如我们现在有个这样的需求,我们要实现一个这样的函数,函数的参数可以是任何值,返回值就是将参数原样返回,并且参数的类型是 string,函数返回类型就为 string?

你很容易写下:

function getValue(arg:string):string  {
  return arg;
}

现在需求有变,需要返回一个 number 类型的值,你会说,联合类型就完事了:

function getValue(arg:string | number):string | number  {
  return arg;
}

但是这样又有一个问题,就是如果我们需要返回一个 boolean 类型,string 数组甚至任意类型呢,难道有多少个就写多少个联合类型?

是的,我们直接用 any 就行了!

function getValue(arg:any):any  {
  return arg;
}

尽管 any 大法好,很多时候 any 也确实能够解决不少问题,但是这样也不符合我们的需求了,传入和返回都是 any 类型,传入和返回并没有统一

作为一个骚有最求的程序员,我们还能不能有其他解决办法呢?

这个时候就要祭出我们的泛型了

基本使用

泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性

上面的需求,我们如果用泛型来解决的话:

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

泛型的语法是尖括号 <> 里面写类型参数,一般用 T 来表示第一个类型变量名称,其实它可以用任何有效名称来代替,比如我们用NIUBI也是编译正常的

泛型就像一个占位符一个变量,在使用的时候我们可以将定义好的类型像参数一样传入,原封不动的输出

使用

我们有两种方式来使用:

1、定义要使用的类型,比如:

getValue<string>('树哥'); // 定义 T 为 string 类型

2、利用 typescript 的类型推断,比如:

getValue('树哥') // 自动推导类型为 string

多个参数

其实并不是只能定义一个类型变量,我们可以引入希望定义的任何数量的类型变量。比如我们引入一个新的类型变量 U

function getValue<T, U>(arg:[T,U]):[T,U] {
  return arg;
}

// 使用
const str = getValue(['树哥', 18]);

typescript 给我们自动推断出输入、返回的类型

泛型约束

在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法:

function getLength<T>(arg:T):T  {
  console.log(arg.length); // 报错,不能调用 length 属性
}

因为泛型 T 不一定包含属性 length,那么我想 getLength 这个函数只允许传入包含 length 属性的变量,该怎么做呢

这时,我们可以使用extends关键字来对泛型进行约束

interface Lengthwise {
  length: number;
}

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

使用:

const str = getLength('树哥')
const arr = getLength([1,2,3])
const obj = getLength({ length: 5 })

这里可以看出,不管你是 str,arr 还是obj,只要具有 length 属性,都可以

泛型接口

在定义接口的时候指定泛型

interface KeyValue<T,U> {
  key: T;
  value: U;
}

const person1:KeyValue<string,number> = {
  key: '树哥',
  value: 18
}
const person2:KeyValue<number,string> = {
  key: 20,
  value: '张麻子'
}

泛型类

class Test<T> {
  value: T;
  add: (x: T, y: T) => T;
}

let myTest = new Test<number>();
myTest.value = 0;
myTest.add = function (x, y) {
  return x + y;
};

泛型类型别名

type Cart<T> = { list: T[] } | T[];
let c1: Cart<string> = { list: ["1"] };
let c2: Cart<number> = [1];

泛型参数的默认类型

我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。有点 js 里函数默认参数的意思。

function createArray<T = string>(length: number, value: T): Array<T> {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
    result[i] = value;
  }
  return result;
}

泛型工具类型

typeof

关键词除了做类型保护,还可以从实现推出类型,

//先定义变量,再定义类型
let p1 = {
  name: "树哥",
  age: 18,
  gender: "male",
};
type People = typeof p1;
function getName(p: People): string {
  return p.name;
}
getName(p1);
keyof

可以用来获取一个对象接口中的所有 key 值

interface Person {
  name: string;
  age: number;
  gender: "male" | "female";
}

type PersonKey = keyof Person; //type PersonKey = 'name'|'age'|'gender';

function getValueByKey(p: Person, key: PersonKey) {
  return p[key];
}
let val = getValueByKey({ name: "树哥", age: 18, gender: "male" }, "name");
console.log(val); // 树哥
in

用来遍历枚举类型:

type Keys = "a" | "b" | "c"

type Obj =  {
  [p in Keys]: any
} // -> { a: any, b: any, c: any }
infer

在条件类型语句中,可以用 infer 声明一个类型变量并且对它进行使用。

type ReturnType<T> = T extends (
  ...args: any[]
) => infer R ? R : any;

infer R 就是声明一个变量来承载传入函数签名的返回值类型,简单说就是用它取到函数返回值的类型方便之后使用。

extends

有时候我们定义的泛型不想过于灵活或者说想继承某些类等,可以通过 extends 关键字添加泛型约束。

interface Lengthwise {
  length: number;
}

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

现在这个泛型函数被定义了约束,因此它不再是适用于任意类型:

loggingIdentity(3);  // Error, number doesn't have a .length property

当我们传入合法的类型的值,即包含 length 属性的值时:

loggingIdentity({length: 10, name: '张麻子'}); // 编译正确
索引访问操作符

使用 [] 操作符可以进行索引访问:

interface Person {
  name: string;
  age: number;
}

type x = Person["name"]; // x is string
内置工具类型
  1. Required

将类型的属性变成必选

interface Person {
    name?: string,
    age?: number,
    hobby?: string[]
}

const user: Required<Person> = {
    name: "树哥",
    age: 18,
    hobby: ["code"]
}
  1. Partial
    与 Required 相反,将所有属性转换为可选属性
interface Person {
    name: string,
    age: number,
}

const shuge:Person = {
  name:'树哥'
} // error  Property 'age' is missing in type '{ name: string; }' but required in type 'Person'.

从上面知道,如果必传而我们少穿传了的话,就会报错

我们使用 Partial 将其变为可选

type User = Partial<Person>

const shuge: User={
  name:'树哥'
} // 编译正确
  1. Exclude
    Exclude<T, U> 的作用是将某个类型中属于另一个的类型移除掉,剩余的属性构成新的类型
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number
  1. Extract
    和 Exclude 相反,Extract<T,U> 从 T 中提取出 U。
type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T1 = Extract<string | number | (() => void), Function>; // () =>void

适用于:并集类型

  1. Readonly
    把数组或对象的所有属性值转换为只读的,这就意味着这些属性不能被重新赋值。
interface Person {
  name: string;
  age: number;
  gender?: "male" | "female";
}

let p: Readonly<Person> = {
  name: "hello",
  age: 10,
  gender: "male",
};
p.age = 11; // error  Cannot assign to 'age' because it is a read-only property.
  1. Record
    Record<K extends keyof any, T> 的作用是将 K 中所有的属性的值转化为 T 类型。
type Property = 'key1'|'key2'
type Person = Record<Property, string>;

const p: Person = {
  key1: "hello 啊",
  key2: "树哥",
};
  1. Pick
    从某个类型中挑出一些属性出来
type Person = {
  name: string;
  age:number;
  gender:string
}

type P1 = Pick<Person, "name" | "age">; // { name: string; age: number; }

const user:P1={
  name:'树哥',
  age:18
}
  1. Omit
    与Pick相反,Omit<T,K> 从T中取出除去K的其他所有属性。
interface Person {
  name: string,
  age: number,
  gender: string
}
type P1 = Omit<Person, "age" | "gender">
const user:P1  = {
  name: '树哥'
}
  1. NonNullable
    去除类型中的 null 和 undefined
type P1 = NonNullable<string | number | undefined>; // string | number
type P2 = NonNullable<string[] | null | undefined>; // string[]
  1. ReturnType
    用来得到一个函数的返回值类型
type Func = (value: string) => string;
const test: ReturnType<Func> = "1";
  1. Parameters
    用于获得函数的参数类型所组成的元组类型。
type P1 = Parameters<(a: number, b: string) => void>; // [number, string]
  1. InstanceType
    返回构造函数类型T的实例类型
class C {
  x = 0;
  y = 0;
}

type D = InstanceType<typeof C>;  // C
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值