TypeScript类型体操练习

历史小剧场

这个世界上,有两种人最痛苦,第一种是身居高位者,第二种是身居底层者,第一种人很少,第二种人很多。第一种人叫崇祯,第二种人叫百姓。
而最幸福的,就是中间那拨人,主要工作,叫做欺上瞒下,具体特点是,除了好事,什么都办;除了脸,什么都要。—《明朝那些事儿》

字符串模板与extends/infer

  • extends: 有两个作用:1、接口继承,2、类型判断(这篇文章主要是体现这个作用);
  • infer: 推导泛型参数,只在extends右边使用

案例

type str = "beyond";

// 获开头
type getFirst<T> = T extends `${infer First}${infer Rest}`? First : never;
type FirstLetter = getFirst<str>; // type FirstLetter = "b"

// 获取除开头的部分
type getRest<T> = T extends `${infer First}${infer Rest}` ? Rest : never;
type RestLetters = getRest<str>; // type RestLetters = "eyond"

// 以y分隔
type Split<T> = T extends `${infer Front}y${infer Back}` ? Back : never;
type Splitted = Split<str>; // type Splitted = "ond"
判断开头
type startsWidth<str extends string, prefix extends string> = str extends `${prefix}${infer Rest}` ? true : false;
type startsWidthKong = startsWidth<"kong", "">; // type startsWidthKong = true
type StartsWithBe = startsWidth<str, "be">; //type StartsWithBe = true
type StartsWithC = startsWidth<str, "c">; // type StartsWithC = false
转为首字母大写
type UppercaseFirst<T extends string> = T extends `${infer First}${infer Rest}` ? `${Uppercase<First>}${Rest}` : never;
type UppercaseStr = UppercaseFirst<str>; // type UppercaseStr = "Beyond"
文本替换-替换第一个
type ReplaceOne<str extends string, from extends string, to extends string> = str extends `${infer Front}${from}${infer Rest}` ? `${Front}${to}${Rest}` : str;
type ReplaceStr = ReplaceOne<str, "o", "a">; // type ReplaceStr = "beyand"
键值对转索引 a=1 => {a:1}
type ConvertStrToRecord<str extends string> = str extends `${infer key}=${infer value}` ? { [k in key]: value } : never;
type ConvertStrToRecordStr = ConvertStrToRecord<"a=1">; 
// type ConvertStrToIndexStr = {
    // a: "1";
// }

as 重映射-索引的重命名

索引转大写
interface IOut {
    aaa: 1,
    bbb: 2,
    fun: () => void
}
type UpperKeys<T extends Record<string, any>> = {
    [K in keyof T as (T[K] extends Function ? K : Uppercase<K>)]: T[K]
}
type res = UpperKeys<IOut>;
// type res = {
//     AAA: 1;
//     BBB: 2;
//     fun: () => void;
// }
合并索引
interface IOut2 {
    ccc: 3,
    ddd: 4
}
type CombineIndex<A extends Record<string, any>, B extends Record<string, any>> = {
    [K in keyof A | keyof B] : K extends keyof A ? A[K] : K extends keyof B ? B[K] : never
}
type combine = CombineIndex<IOut, IOut2>;
// type combine = {
//     aaa: 1;
//     bbb: 2;
//     fun: () => void;
//     ccc: 3;
//     ddd: 4;
// }

巧用递归

文本替换-替换所有
type ReplaceAll<Str extends string, From extends string, To extends string> = Str extends `${infer Front}${From}${infer Rest}` ? `${Front}${To}${ReplaceAll<Rest, From, To>}` : Str;
type ReplaceAllStr = ReplaceAll<"12333333456", "3", "A">; // type ReplaceAllStr = "12AAAAAA456"
字符串反转
type Reverse<Str extends string, Res extends string = ""> = Str extends `${infer First}${infer Rest}` ? Reverse<Rest, `${First}${Res}`> : Res;
type ReverseStr = Reverse<"hello">; // type ReverseStr = "olleh"

综合

字符串解析-初级

例如:我们要将 a=1&b=2&c=3 转为 {a:1, b:2, c:3}
那么,我们要有按这三步走

  1. &分割取 键值对 处理
  2. 处理单个的 键值对 转换为 索引类型
  3. 把 转换后的 索引类型 合并
type Parse<Str extends string, Res extends Record<string, any> = {}> = 
  Str extends `${infer One}&${infer Rest}` ?
  Parse<Rest, CombineIndex<Res, ConvertStrToRecord<One>>> :
  CombineIndex<Res, ConvertStrToRecord<Str>>

type parseToRecord = Parse<"a=1&b=2&c=3">; // type parseToRecord = {a: "1", b: "2", c: "3"}
字符串解析-升级
  • 如果只有键没有值,则返回 {key: true};
  • 合并重复索引。例如 a=1&a=2 转为 {a: [‘1’, ‘2’]};
  • 合并的值不能重复。例如 a=1&a=2&a=2 转为 {a: [‘1’, ‘2’]};
type ConvertStrToRecordUp<Str extends string> = Str extends `${infer Key}=${infer Value}` ? 
    { [K in Key] : Value } : Str extends `${infer Key}` ? { [K in Key]: true } : {};
type ConvertStrToRecordUpStr = ConvertStrToRecordUp<"a">;
// type ConvertStrToRecordUpStr = {
//     a: true;
// }

// 合并重复索引
type CheckDuplicate<A extends Record<string, any>, B extends Record<string, any>> = keyof B extends keyof A ? AddR<A, B> : CombineIndex<A, B>;
type AddR<A extends Record<string, any>, B extends Record<string, any>> = {
    [K in keyof A] : K extends keyof B ?
    CheckInclue<A[K], B[K]> extends true ? A[K] :  // 重复索引,合并值,值不能重复
    A[K] extends any[] ? [...A[K], B[K]] : [A[K], B[K]] : A[K]
}

type CheckInclue<A extends any[], B extends string> = A extends [infer First, ...infer Rest] ? 
    First extends B ? true : CheckInclue<Rest, B> : false;
type checkIncludeDemo = CheckInclue<["a", "b", "c"], "a">; // type checkIncludeDemo = true


type ParseUp<Str extends string, Res extends Record<string, any> = {}> = 
  Str extends `${infer One}&${infer Rest}` ?
  ParseUp<Rest, CheckDuplicate<Res, ConvertStrToRecordUp<One>>> :
  CheckDuplicate<Res, ConvertStrToRecordUp<Str>>

type parseToRecordUp = ParseUp<"a=1&b&a=2&a=3&c&d=4&e&e=10&a=3">; 
// type parseToRecordUp = {
//     b: true;
//     c: true;
//     a: ["1", "2", "3"];
//     e: [true, "10"];
//     d: "4";
// }
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值