这么装13的 TS 用法,我劝你别在项目里边运用,除非你够皮。
废话不多说,直接看题目
将给定字符串类型
QueryStr
类型通过Split
解析为键值结构类型。
分析
js
假如这是面试题,我可能会忽然肚子有点不舒服,TS 类型搞不定,换成 js 应该没什么问题吧?
const queryStr = 'name=kn&sex=f&language=ts';
const splitStr = queryStr.split('&');
const queryKeyValue = {};
splitStr.forEach( str =>{
const [key, value] = str.split('=');
queryKeyValue[key] = value;
})
console.log(queryKeyValue); // {name: 'kn', sex: 'f', language: 'ts'}
TS type
从 js 的实现来看,里边主要用到了 split
和遍历,TS 怎么实现呢?
这里会引进一个日常开发中很少见的 TS 用法 infer,infer 最早出现在这个 PR 中,表明在 extends 条件语句中待推断的类型变量,假如对泛型还不了解的同学,建议先去看下 TS 基础再回来
。
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : T;
作者给的示例是用于获取返回值类型,怎么运用呢?先分析类型定义,当咱们传入的 T 满足 (…args: any[]) => type 时,咱们得到的类型便是 type。
如下,因为咱们传入的 T (params: string) => number
,是满足需求的,所以最终推断出来的 count 类型是 type,也即是 number。
const count: ReturnType<(params: string) => number> = 1
再看个简单的示例:
type IType<T> = T extends Array<infer S> ? S[] :T
const str: IType<string> = 'a'; // string
const arr: IType<Array<string>> = ['a', 'b']; // string[]
回到正题,咱们逐步分化:
type QueryStr = 'name=kn&sex=f&language=ts';
type Split<T> = T extends `${infer Left}&${infer Right}` ? [Left, ...Split<Right>] : [T]
type QueryKeyValue = Split<QueryStr>; //["name=kn", "sex=f", "language=ts"]
通过这步,咱们得到了类似 js str.split('&')
的效果,仔细的你或许已经注意到,咱们在里边用到了递归的思想。
下一步是怎么将 ["name=kn", "sex=f", "language=ts"]
转换为 [{name: "kn";},xx]
?
type StrToKeyValue<T> = T extends `${infer Left}=${infer Right}` ? {[key in Left]: Right} : T
type KeyValueTuple<T> = T extends [infer Left, ...infer Right] ? [StrToKeyValue<Left>, ...KeyValueTuple<Right>] : T
type QueryKeyValue = KeyValueTuple<Split<QueryStr>>; // [{name: "kn";},{sex: "f";},{language: "ts";}]
这步完结之后,其实剩余的就简单了,这里提示大家一个点,type IQuery = {name: string; age: number}
和 type IQuery = {name: string} & {age: number}
是等价的。
type TupleToIntersection<T> = T extends [infer Left, ...infer Right] ? Left & TupleToIntersection<Right> : unknown
type QueryKeyValue = TupleToIntersection<KeyValueTuple<Split<QueryStr>>>
至此,如此复杂(反常)的类型转换完结,咱们验证一下。
const queryKeyValue: QueryKeyValue = {
name: 'kn',
sex: 'f',
language: 'ts'
}
发现并没有 TS 语法错误,鼠标移动到 QueryKeyValue
上也能看到对应类型
总结
看完有没有后背发凉,赶紧手写试试吧,上面为了分化步骤,写的有点复杂了,咱们看下优化后的代码。
/**
* TS 装13用法
*
* eg:
* type QueryStr = 'name=kn&sex=f&language=ts'
*
* ........ split 解析 .......
*
* type QueryKeyValue = Split<QueryStr>
*
* ........ res .........
*
* type QueryKeyValue = {
* name: 'kn';
* sex: 'f';
* language: 'ts';
* }
*/
type QueryStr = 'name=kn&sex=f&language=ts';
type Split<T> = T extends `${infer Left}&${infer Right}` ? [Left, ...Split<Right>] : [T];
type TupleStrToIntersection<T> = T extends [`${infer Left}=${infer Right}`, ...infer Rest]
? { [key in Left]: Right } & TupleStrToIntersection<Rest>
: unknown;
type QueryKeyValue = TupleStrToIntersection<Split<QueryStr>>;