Skip to content

泛型

概念泛型就是把类型当成参数,可以在函数、接口、类、别名中使用

泛型: <> 里写类型参数,一般可以用 T 来表示。

1.函数泛型

先看定义的语法

ts
// 定义
function fn<T>(num: T): T{
    return num
}

// 调用
fn<number>(num)

定义:

  • 泛型需要使用<>,放在函数名与()的中间,用来接受函数调用时,传来的类型
  • <>里通常使用大写字母,比如T(type),来占位,相当于函数的形参,后面函数中可以使用这个类型
  • 多个类型用,隔开,比如<T, U, M, N>

调用:

  • 调用时也是在()的前面加上了一个<>,此时的<>用来传递我们真正的参数
  • 同样若有多个类型用,隔开,如<number,string,boolean>

TIP

函数泛型在开发中会广泛使用到,比如Vue3ref()reactive()reactuseState(),他们都需要使用泛型传入一个类型参数,来确定返回值的类型

ts
const num=ref<number>(123)

const obj=reactive<{num:number}>({
    num: 123
})


interface Item{
    name:string
}
const [arr,setArr]=useState<Array<Item>>([
    {
        name:'ikun'
    }
])

2.接口泛型

语法如下

ts
interface Box<Type> {
  contents: Type;
}

let box: Box<string>;

3.类泛型

ts
class Test<T> {
	attr: T
	constructor(opt:T) {
		this.attr = opt
	}

	getAttr() {
		return this.attr
	}
}

const test = new Test<number>(123)

console.log(test.getAttr())  // 123

4.数组泛型

数组除了可以通过const arr:number[]=[1,2,3]方式定义,也可以通过泛型

ts
const arr:Array<number> = [1,2,3]

在 TypeScript 内部,Array是一个泛型接口,类型定义基本是下面的样子。

ts
interface Array<Type> {
  length: number;

  pop(): Type | undefined;

  push(...items: Type[]): number;

  // ...
}

Type就是我们在定义数组时,传入的类型

除了可以定义基本数据类型数组,也可以定义复杂类型的数组,开发中通常配合接口使用

ts
interface Item{
	name: string;
	age:number
}


const arr: Array<Item> = [
	{
		name: 'cai',
		age:18
	}
]

TIP

除此之外,Ts中还有一些其他的数据结果接口,如比如MapSetPromise,其实也是泛型接口,完整的写法是Map<K, V>Set<T>Promise<T>

5.类型参数默认值

类型参数可以设置默认值。使用时,如果没有给出类型参数的值,就会使用默认值。

typescript
function getFirst<T = number>(num:T): T {
  return n;
}

6.类型参数约束条件

很多类型参数并不是无限制的,对于传入的类型存在约束条件。

typescript
function comp<Type>(a: Type, b: Type) {
  if (a.length >= b.length) {
    return a;
  }
  return b;
}

上面示例中,类型参数 Type 有一个隐藏的约束条件:它必须存在length属性。如果不满足这个条件,就会报错。

TypeScript 提供了一种语法,允许在类型参数上面写明约束条件,如果不满足条件,编译时就会报错。这样也可以有良好的语义,对类型参数进行说明。

typescript
function comp<T extends { length: number }>(a: T, b: T) {
  if (a.length >= b.length) {
    return a;
  }
  return b;
}

上面示例中,T extends { length: number }就是约束条件,表示类型参数 T 必须满足{ length: number },否则就会报错。

typescript
comp([1, 2], [1, 2, 3]); // 正确
comp("ab", "abc"); // 正确
comp(1, 2); // 报错

上面示例中,只要传入的参数类型不满足约束条件,就会报错。

类型参数的约束条件采用下面的形式。

typescript
<TypeParameter extends ConstraintType>

上面语法中,TypeParameter表示类型参数,extends是关键字,这是必须的,ConstraintType表示类型参数要满足的条件,即类型参数应该是ConstraintType的子类型。

类型参数可以同时设置约束条件和默认值,前提是默认值必须满足约束条件。

typescript
type Fn<A extends string, B extends string = "world"> = [A, B];

type Result = Fn<"hello">; // ["hello", "world"]

上面示例中,类型参数AB都有约束条件,并且B还有默认值。所以,调用Fn的时候,可以只给出A的值,不给出B的值。

另外,上例也可以看出,泛型本质上是一个类型函数,通过输入参数,获得结果,两者是一一对应关系。

如果有多个类型参数,一个类型参数的约束条件,可以引用其他参数。

typescript
<T, U extends T>
// 或者
<T extends U, U>

上面示例中,U的约束条件引用T,或者T的约束条件引用U,都是正确的。

但是,约束条件不能引用类型参数自身。

typescript
<T extends T>               // 报错
<T extends U, U extends T>  // 报错

上面示例中,T的约束条件不能是T自身。同理,多个类型参数也不能互相约束(即T的约束条件是UU的约束条件是T),因为互相约束就意味着约束条件就是类型参数自身。

MIT Licensed