继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

超越类型安全:打造更智能的TypeScript,从运行时属性选择器开始

LEATH
关注TA
已关注
手记 484
粉丝 93
获赞 467

这里有个声明请看
请仔细阅读

嘿,在我们开始之前,让我先澄清一点:虽然我会花很多时间谈论我的这个工具包 ts-runtime-picker ,但这并不是一篇推广文章。我只是分享我的经历和构建过程。 (不过嘿,如果你感兴趣,这是链接 😄)here's the link

……此处省略了部分内容

TypeScript 如何让我有了新的想法(以及一个包的诞生)

让我们退一步。其实,我用 TypeScript 已经有一段时间了。我可不敢自称 TypeScript 大师,但我在公司里用它完成了一些大型项目。你知道,通常会有一些“Hello World”项目和稍微复杂一点的项目,当然还有些常规的“Hello World”项目。当然也少不了几次在谷歌上搜索“这个错误提示是什么意思”或“如何从接口里挑选字段”(你懂的。😉)

有一天,我在使用 Firebase 云函数工作时遇到了一个问题。我在 createUser 端点上编写验证逻辑,清理数据并进行数据校验,并处理常规的 CRUD 请求混乱。一切进展都很顺利,直到我遇到了前一位开发者留下的这段代码:

// 将用户数据添加到Firebase的“users”集合中
firebase.collection("users").add(request.data.user);

全屏 退出全屏

...而我心中的 TypeScript 大神在大喊。 🚨

我说,喂喂,这可真是个大红灯。对吧?直接插入未经过滤的用户数据非常冒险——如果请求的数据缺少验证,岂不是很容易把不需要的字段加进去?太不妙了。

我很快移除了代码,但愣了一下。🤔 我心想:“等等,如果我将 request.data 赋值给 User 接口,TypeScript 会阻止我这样做吗?这样不是就可以了吗?”(满怀希望地看了一眼 IDE,等着 IDE 显示红色波浪线。)

但等等…… TypeScript 并不是 魔法。它只是在编译时进行检查,对吗?它在运行时不起作用。TypeScript 只是提供类型安全的一种手段,但它并不会在代码运行时强制执行任何事情。它并不能真正阻止在运行时添加额外字段。

所以我给我的一个队友打了电话,问道:“嘿,兄弟,如果我们有一个叫 alphabets 的对象,包含了所有字母,并且我们创建了一个接口 OnlyTwoLetters,只允许字母 'a' 和 'b',当我们把 alphabets 对象强制转换成这个接口时会发生什么?”

    // 包含所有字母的字典
    const alphabets = {
      a: 'Apple',
      b: 'Banana',
      c: 'Cherry',
      d: 'Date',
      e: 'Eggplant',
      f: 'Fig',
      // 以此类推直到字母z
    };

    // 只允许'a'和'b'的接口定义
    interface OnlyTwoLetters {
      a: string;
      b: string;
    }

    // 将alphabets对象转换为OnlyTwoLetters接口类型
    const filteredAlphabets = alphabets as OnlyTwoLetters;

    // 打印filteredAlphabets

进入全屏,退出全屏

立刻回应说:“哈哈,但你还是会收到所有的字母,不过TypeScript在运行时也拦不住这些字母。”

果然。我早知道。我抱着希望——希望 TypeScript 能神奇地避免我在运行时犯错。🙄

但就在那时,我想到:如果 TypeScript 能够在运行时强制执行这一点,如果我们能够将一个对象转换成特定接口的对象,并让 TypeScript 自动剔除任何未在该接口中定义的属性呢?

那样一来,我的问题就解决了。

此处省略内容

终于迎来了ts-runtime-picker的诞生了

所以,有了这个想法,我想:“为什么不把这个想法实现呢?”如果我可以将 request.data 转换为 User 接口,TypeScript 就可以帮我 自动 去掉多余的属性,使对象安全地插入到 Firebase 数据库。🎉

就这样子,这个想法诞生了。目标很简单:开发一个包,让使用 TypeScript 的用户可以根据特定接口定义的字段过滤掉不需要的属性。

最棒的是?它能让我省去手动验证和筛选字段的麻烦。手动验证的日子再也不用担心了。

    const filteredData = {
      name: requestData.name,
      age: requestData.age,
    };

    firebase.collection("users").add(filteredData);  // 工作多,乐趣少。

切换到全屏 退出全屏

此处省略了部分内容

它是怎么工作的:让 TypeScript 大展身手

使用 ts-runtime-picker ,整个过程将会自动进行。你可以将一个对象转换为...的形式,该包会确保只保留那些在接口中定义的属性。实际操作如下:

之前:手动检

    interface User {
      name: string;
      age: number;
    }

    const requestData = { name: 'John', age: 30, address: '123 Street' };

    // 移除不需要的字段,只保留必要的信息:
    const filteredData = {
      name: requestData.name,
      age: requestData.age,
    };

    firebase.collection('users').add(filteredData);  // 不是很美观。

进入全屏模式。退出全屏。

使用 ts-runtime-picker

    import { createPicker } from "ts-runtime-picker";

    interface User {
      name: string;
      age: number;
    }

    const requestData = { name: 'John', age: 30, address: '123 Street' };

    // 自动过滤掉不存在的属性,使之更安全:
    const picker = createPicker<User>();
    const safeData = picker(requestData);

    firebase.collection('users').add(safeData);  // 这使得代码更加简洁!
    // 这样就把数据添加到用户集合中了。

全屏模式 退出全屏

最好的部分?这段代码默认是安全的。无需手动检查或操作对象,ts-runtime-picker 会自动为你处理这一切,通过过滤掉所有不在 User 接口中的字段来实现。你可以专注于核心逻辑,无需担心意外插入字段的问题。🙌

此处省略部分内容

懒惰的魔力(以及懒惰如何激发创新)

所以,你可能在想:“这是纯粹因为偷懒造成的吗?” 对此,我的回答是:其实,是,但也并非全然如此。 😅

我有点偷懒,不想每次插入数据时都手动筛选字段。但是有时候,这种想让事情简单一些的欲望也能带来意想不到的好结果,甚至能激发创新。

事实上,尽管一开始有点“偷懒”,我还是花了8小时来制作这个包。确实如此,就是这么回事。 😆

但有时候就是这样子。“需求催生了发明”,避免繁琐重复检查的需求催生了一个新的解决方案,这最终让我的生活以及其他许多人变得轻松很多。

所以,其实是因为懒惰才让事情开始,但解决问题的需要促成了ts-runtime-picker。有时候,遇到困难或懒惰其实并不一定是坏事——这可能是新事物和有用之物的起源!


结尾

这便是ts-runtime-picker这个包背后的故事。从TypeScript的挫败感开始,创造了一个能解决实际问题的工具。从TypeScript的局限性中找到了突破。这个包是为了帮助TypeScript用户充分利用类型安全性而设计的,不仅在开发过程中,而且在运行时也能确保类型安全。

如果你厌烦了手动筛选字段的烦恼,或者担心会有不需要的数据混进来,不妨试试 ts-runtime-picker 试试看。这样你就少了一件烦心事,让你在使用 TypeScript 时更省心。😄

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP