官方文档中这样描述:
在像 C# 和 Java 这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
恒等函数:返回一个任何传进内容的函数 。
如果想要恒等函数一个具体的类型可能会这么写:
function fn(arg:number):number{
return arg;
}
// 或者
function fn(arg:string):string{
return arg;
}
// 又或者
function fn(arg:any):any{
return arg;
}
尽管使用 any
类型可以让我们接受任何类型的 arg
参数,但也让我们丢失了函数返回时的类型信息。
基本使用
使用泛型来定义上述函数:
- 泛型的语法是
<>
里写类型参数(类型变量),一般可以用T
来表示或者其他任何表示。T
相当于一个占位符或者变量,在使用时把定义的类型像参数传入。 - 泛型可以在定义函数、接口或类时不预先指定类型,而是使用时指定类型。
function fn<T>(arg:T):T{
return arg;
}
// 调用方式
fn(10); // 类型参数推断, 编译器通过传入的变量来判断属于哪种类型
fn<string>("abc"); // 显示的定义传入的变量类型为string
指定多个参数:
// 指定多个类型
function fn2<T,K>(a:T,b:K):T{
return a;
}
fn2(12,"ab")
泛型约束
泛型在成员之间提供有意义的约束,这些成员可以是:函数参数、函数返回值、类的属性、类的方法等。
如下:在函数内部使用函数变量时由于不清楚函数的类型,所以不能随便操作它的属性和方法
function getLength<T>(arg:T){
console.log(arg.length) // 报错
return arg
}
为此 ,我们需要定义一个接口,用来描述约束
interface lengthFn{
length:number;
}
function getLength<T extends lengthFn>(arg:T){
console.log(arg.length)
return arg
}
// 只要有 length 属性都可以调用
getLength([1,2,3])
getLength({name:11,length:2})
泛型接口
定义接口的时候指定泛型。
interface commonFn<T> {
(arg:T):T;
}
let myFun:commonFn<string> = function (aa){
return aa;
}
myFun("abc");
function identity<Type>(arg: Type): Type {
return arg;
}
let numberFun:commonFn<number> = identity;
numberFun(100);
interface commonFn<T = number> { // 给泛型加默认参数
(arg:T):T;
}
泛型类
在类名后面,使用尖括号中 <>
包裹住类型参数列表:确保类中所有属性都使用了相同的类型。
class TestNumber<T>{
value:T;
add:(a:T,b:T)=>T
}
let myTestNumber = new TestNumber<number>();
myTestNumber.value = 1;
myTestNumber.add=(a:number,b:number)=>{
return a + b;
};
泛型工具类型
- typeof 类型推断
// typeof 进行类型推断
const p1= {name:"tom",age:12};
type people = typeof p1;
- keyof 获取对象中所有的 key 值
// keyof
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: "tom", 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;
内置工具类型
pick
从一个类型中摘取某一部分属性
// eg:公共的类型
interface userInfo{
name:string;
password:string;
id:number;
age:number;
}
// 只想摘取一部分属性作为类型约束
type loginDataType = Pick<userInfo,'name'|'password'>
const loginData:loginDataType ={
name:"tom",
password:"123"
}
omit
从一个类型剔除某些属性
// eg:公共的类型
interface userInfo{
name:string;
password:string;
id:number;
age:number;
}
// 剔除一部分属性, 从哪个对象中剔除,剔除哪些属性
type resDataType =Omit<userInfo,'password'>
const resData:resDataType={
name:"jack",
id:1,
age:22
}
interface 和type 区别
type 类型别名
给你的类型起一个新名字,可以定义原始值、联合类型、元组、对象、函数以及其他任务你需要手写的类型。
// type 定义函数
type getAddType =(a:number,b:number)=>number;
const add:getAddType =(a:number,b:number)=>{
return a + b;
}
// 定义对象
type PersonA = {
name:string;
age:number;
}
// 报错 type 不能重复定义
type PersonA = {
gender:string;
}
// 基本类型
type Name = string;
type Age = number;
// 联合类型
type Item = Name | Age;
const aaa:Item = 1;
// 交叉类型 继承
type StudentA = PersonA & { class:string };
// 元组类型
type StudentAndTeacherList = [StudentA, {gender:string}]
// 类型别名扩展接口
type StudentPro = PersonA & {
class:string;
}
interface 接口
interface 只能定义对象和函数。
// interface 定义对象
interface Person {
name:string;
age:number;
}
// 可重复声明,会合并类型
interface Person{
gender:string;
sayHello?:void;
}
let p:Person = { name:"John", age:20, gender:"女" };
// interface 定义函数
interface getAddType{
(a:number,b:number):number;
}
const add:getAddType =(a:number,b:number)=>{
return a + b;
}
// 继承
interface Student extends Person {
class:string
}
let ss:Student = { name:"John", age:20, gender:"女",class:'1' }
// 接口扩展类型别名
interface Sister extends Item {
name: string;
}
区别:
-
type 可以定义原始值、联合类型、元组、对象、函数以及其他任务你需要手写的类型。
interface 只能定义函数和方法。
-
type 不能重复定义
interface 可重复定义,会合并声明
-
type 和 interface 都允许继承,可以混合继承,也就是说
interface
可以扩展type
,type
也可以扩展interface
。接口的扩展是继承(extends
)。类型别名的扩展就是交叉类型(通过&
实现)。
小结
- 泛型可以定义重用组件,在使用时传入不同类型来使用组件。
- 泛型是指在定义函数、接口、类时可以不预先指定类型,而是使用时确定类型。
- 泛型一般用
<T>
来表示,T
相当于一个占位符或者变量,当使用时才将类型当入参数传入。 - 泛型约束: 在成员之间提供了有意义的约束,这些成员可以是 函数参数,函数返回值,类的属性,类型的方法等。