类型警卫

将类型带入运行时间
此自封自封库将为您提供工具, 将类型验证移动到您的应用程序边缘, 移动到您的 IO 环上, 您可以在此验证来自外部的数据, 并信任其它应用程序 .
何时相信你的数据
在JavaScript中,一个相当常见的反模式是检查你代码库中的每一处一个值的类型,我们叫这个 防御程序。 。 。 . 很可能你遇到这样的密码:
const someFunction = (value) => {
if (value !== undefined) throw Error("value must be defined");
return anotherFunction(value, "I already checked value")
};
const anotherFunction = (value, message) => {
if (value !== undefined) throw Error("value must be defined");
return value + message;
};
当在您的应用程序中多次检查其类型时,这意味着 您的代码不相信它的数据。 。 。 .
虽然 偏执编程 听起来可能很酷,它不是, 它远远没有生产,甚至安全.
应用程序代码应当关注其域名,功能,模块或类不应检查其参数类型是否有效.
不过 并非所有数据都是安全的 我们的代码基础应该信任内部数据,但 必须 请检查外部数据是否为恶意、腐败或只是格式错误.
校验应在我们应用程序的最外层接口中进行 IO 层。 。 。 .
IO层依应用程序的不同而存在于不同的地方:
- 在图书馆或框架中:公共方法和职能
- 在服务器应用程序中: HTTP 和数据库请求
- 在网络应用程序中: HTTP 响应,本地存储和用户输入
应用程序与外部数据相接之处
在此层上进行所有的数据验证将允许核心应用程序信任数据正在使用,允许您删除冗余的类型检查并解开您的代码 .
类型警卫
类型守护是检查值并返回的函数 真实 如果值为特定类型:
type Guard<T extends V, V = unknown> = (v: V) => v is T;
例如, 此虚构检查显示未定义的值是有效的 Grade
type Grade = "A" | "B" | "C" | "D" | "F";
const isGrade = (value: unknown): value is Grades => ["A","B","C","D","F"].includes(value)
命名: 类型警卫从 实值 然后检查类型
警卫工厂
警卫工厂是用来送回一类警卫的
高级警卫
高阶卫士是接收类型卫士并返回复合类型的另一个类型卫士的功能:
const ArrayOf= <T>(type: Guard<T>) => {
return (arr: unknown): arr is Array<T> => Array.isArray(arr) && arr.every((v) => type(v));
};
命名: 高命令卫士从基底类型起首,以 员额
实例
HTTP 回复
当从学生中获取数据时, API 在允许输入应用程序前进行类型检查 :
import { ArrayOf, ObjectOf, isString, isNumber, isBoolean } from "@gabrielurbina/type-guard";
type Student = { firstName: string; lastName: string; age: number; active: boolean };
const isStudent = ObjectOf({
firstName: isString,
lastName: isString,
age: isNumber,
active: isBoolean
});
const isStudentList = ArrayOf([isStudent]);
export const fetchStudents = async (id: string): Promise<Student[]> => {
// Remember to treat external data as unknown
const response: unknown = await(await fetch(`api/students?class=${id}`)).json();
if(!isStudentList(response)) throw Error("Invalid API response");
return response
}
本地存储
当获得用户存储的首选项时 localStorage 如果类型无效或没有存储偏好,则在使用它之前先检查它或使用一些默认偏好 :
import { ObjectOf, ValueOf, isNumber, isBoolean } from "@gabrielurbina/type-guard";
type Theme = "dark" | "light";
type Currency = "USD" | "EUR" | "GBP";
interface UserPreferences {
theme: Theme;
currency: Currency;
timeZoneOffset: number;
finishedWalkthrough: boolean;
};
const isUserPreferences = ObjectOf({
theme: ValueOf<Theme>(["dark" , "light"]),
currency: ValueOf<Currency>(["USD" , "EUR" , "GBP"]),
timeZoneOffset: isNumber,
finishedWalkthrough: isBoolean,
});
const defaultUserPreferences: UserPreferences = {
theme: "light",
currency: "USD",
timeZoneOffset: 0,
finishedWalkthrough: false,
}
export const retrieveUserPreferences = (): UserPreferences => {
const storedPreferences = localStorage.getItem("user-preferences");
if (storedPreferences === null) return defaultUserPreferences;
// Treat external data as unknown
const preferences: unknown = JSON.parse(storedPreferences);
if(!isUserPreferences(preferences)) return defaultUserPreferences;
return preferences;
}