这么装13的TS用法,我还是第一次见

这么装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[] :Tconst 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> = Textends `${infer Left}=${infer Right}` ? {[key in Left]: Right} : Ttype KeyValueTuple<T> = T extends [infer Left, ...infer Right] ? [StrToKeyValue<Left>, ...KeyValueTuple<Right>] : Ttype 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>>; 

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Vue3 中,自定义指令的钩子函数与 Vue2 有所不同,其中 `bind` 钩子已经被废弃,取而代之的是 `beforeMount`、`mounted`、`beforeUpdate`、`updated`、`beforeUnmount`、`unmounted` 等钩子函数,它们的执行时机也有所不同。 如果你在 `mounted` 钩子中修改 DOM 样式,那么样式修改会在组件挂载到页面后才生效。如果你希望样式修改能够在页面初始化时就生效,可以考虑在 `beforeMount` 钩子中执行修改操作。例如: ```typescript import { DirectiveBinding } from 'vue'; // 定义自定义指令 const myDirective = { beforeMount(el: HTMLElement, binding: DirectiveBinding) { // 修改 DOM 样式 el.style.padding = binding.value; } }; // 在组件中使用自定义指令 <template> <div v-my-directive="padding">Hello World</div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ data() { return { padding: '10px' }; }, directives: { myDirective } }); </script> ``` 如果在 `beforeMount` 钩子中修改样式仍然无法立即生效,可以考虑使用 `this.$nextTick()` 方法,在下一个 DOM 更新周期中再进行样式修改,例如: ```typescript import { DirectiveBinding } from 'vue'; // 定义自定义指令 const myDirective = { beforeMount(el: HTMLElement, binding: DirectiveBinding) { this.$nextTick(() => { el.style.padding = binding.value; }); } }; // 在组件中使用自定义指令 <template> <div v-my-directive="padding">Hello World</div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ data() { return { padding: '10px' }; }, directives: { myDirective } }); </script> ``` 在这个例子中,我们在 `beforeMount` 钩子中使用 `this.$nextTick()` 方法,将样式修改操作放到下一个 DOM 更新周期中执行,确保 DOM 已经被渲染。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值