这里有个声明请看
请仔细阅读
嘿,在我们开始之前,让我先澄清一点:虽然我会花很多时间谈论我的这个工具包 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 时更省心。😄