关于typescript中的extends和infer以及用法
创始人
2024-02-04 19:55:21

extends

extend的意思如果不是用在类的扩展中那么就表示约束在

type Pick = {[P in K]: T[P];
};

比如下面这个例子:
在这里插入图片描述

在Picks中K应该约束在keyof T这个类型中。

infer

infer表示占位符

逆变和协变

协变:子类型赋给父类型
逆变:父类型赋给子类型(设置strictFunctionTypes:true)
双向协变:父类型可以赋给子类型,子类型也可以赋给父类型

{}、Object和object

{} 是个空对象,没有索引,如果将某些内容注释为{},则可以是任何一种类型。
object 不能接收原始类型,而 {} 和 Object 都可以,这是它们的区别。
object 一般会用 Record 代替,约束索引类型更加语义化

&交叉类型

合并两个类型

interface IPerson {id: string;age: number;
}interface IWorker {companyId: string;
}type IStaff = IPerson & IWorker;const staff: IStaff = {id: 'E1006',age: 33,companyId: 'EXE'
};console.dir(staff)

如果是非对象:

type res = 1 & string

在这里插入图片描述
此时合并的是never,所以非对象类型合并的必须是同类型

??和?可选链

??表示不为null并且不为undefined

const data = {name:1
}
const dong = data.name ?? 'dog';
// 编译后
"use strict";
var _a;
const data = {name: 1
};
const dog = (_a = data.name) !== null && _a !== void 0 ? _a : 'dog';

可以看到是表示不为null和undefined才会获取data.name否则是’dog’。
?表示为null或者是undefined和??刚好相反

const data = {name:1
}
const dong = data?.name;
// 编译后
"use strict";
const data = {name: 1
};
const dong = data === null || data === void 0 ? void 0 : data.name;

可以看到?首先判断是不是null或者undefined,如果是则返回undefined,否则返回data.name。

keyof any

在这里插入图片描述

infer和extends

infer只能在extends关键字为true的一侧
infer x可以理解成一个未知数x,表示待推断的函数参数

type Test = T extends (infer X)[] ? X : never;
// a类型为number | string
let a: Test = '10'

接下来带大家分析一个比较好的例子:

type ParseQueryString= Str extends `${infer Param}&${infer Rest}`// Param--a=1  Rest--b=2&c=3 // { a:1 }? MergeParams, ParseQueryString>: ParseParam;// 将a=1这种格式解析为对象{a:1}
type ParseParam =// a=1  Key--a  Value--1 // { a:1 }Param extends `${infer Key}=${infer Value}`? {[K in Key]: Value} : Record;// {a:1} {b:2,c:3} 用所有的key做对象,如果只是在其中一个对象那么就直接返回,否则合并两个对象的值
type MergeParams,OtherParam extends Record
> = {// ['a','b','c']readonly [Key in keyof OneParam | keyof OtherParam]:// 'a' 约束在{a:1}Key extends keyof OneParam// 'a'是否约束在{b:2,c:3}?(Key extends keyof OtherParam// 如果'a'同时约束在{a:1}和{b:2,c:3}那么就合并值返回一个列表? MergeValues// 否则返回{a:1}中的1: OneParam[Key]):// 'a'是否约束在{b:2,c:3}中,在就取出值否则不返回(Key extends keyof OtherParam? OtherParam[Key]: never)}type MergeValues =// 两个一样One extends Other? One// other是个列表: Other extends unknown[]// 合并列表? [One, ...Other]// 直接返回一个列表: [One, Other];function parseQueryString(queryStr: Str): ParseQueryString {if (!queryStr || !queryStr.length) {return {} as any;}const items = queryStr.split('&');const queryObj: any = {};items.forEach(item => {const [key, value] = item.split('=');if (queryObj[key]) {if (Array.isArray(queryObj[key])) {queryObj[key].push(value);} else {queryObj[key] = [queryObj[key], value]}} else {queryObj[key] = value;}});return queryObj
}const res = parseQueryString('a=1&b=2&c=3')
console.log(res);

在这里插入图片描述
使用infer实现递归:

type ReverseStr = Str extends `${infer First}${infer Rest}`? ReverseStrFirst}${Result}`>: Result;const a = 'hello'
type b = ReverseStr

在这里插入图片描述

/*
Str = hello Result = '' First = h Rest = ello
Str = ello Result = 'h' First = e Rest = llo
Str = llo Result = 'eh' First = l Rest = lo
Str = lo Result = 'leh' First = l Rest = o
Str = o Result = 'lleh' First = o Rest = ''
Str = '' Result = 'olleh' First = '' Rest = ''*/

下面我们来看看综合案例:

加法

type BuildArray = Arr['length'] extends Length? Arr: BuildArray;type Add =[...BuildArray, ...BuildArray]['length'];type addResult = BuildArray<10>

减法:

type Subtract = 
// 模式匹配占取部分值BuildArray extends [...arr1: BuildArray, ...arr2: infer Rest]? Rest['length']: never;
type dResult = Subtract<10,9>

乘法

type Mutiply = Num2 extends 0 ? ResultArr['length']: Mutiply, [...BuildArray, ...ResultArr]>;type mResult = Mutiply<11,10>

除法

type Divide = Num1 extends 0 ? CountArr['length']: Divide, Num2, [unknown, ...CountArr]>;

数组长度

type StrLen = Str extends `${string}${infer Rest}` ? StrLen : CountArr['length']

大于

type GreaterThan = Num1 extends Num2 ? false// CountArr长度是否等于Num2: CountArr['length'] extends Num2? true// CountArr长度是否等于Num1: CountArr['length'] extends Num1? false// 不断的加1去判断是否和Num1或者Num2相等,如果先和Num2相等,那就说明Num1是大于Num2的: GreaterThan;

过滤

type FilterString = {[Key in keyof T as T[Key] extends string ? Key: never]: T[Key];
}

as表示重命名,返回 never 代表过滤掉,否则保留。

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...