繁体   English   中英

如何使用 TypeScript 验证 object 与接口没有重叠属性?

[英]How do I use TypeScript to validate that an object has no overlapping properties with an interface?

我有一个接口V和一个类型未知的 object x 如果x的任何属性是V的键,我想抛出运行时错误。 这样做最友好的 TypeScript 方式是什么?

这是一个明显的例子:

interface V {
  a: number;
  b: string;
}

const x = getDataFromRequest();

if (hasInvalidProperties(x)) {
  throw new Error();
}

我知道使用 JavaScript 有上百万种方法可以做到这一点(例如,保留无效字符串的数组等),尽管我希望利用类型系统来隐式执行此检查。


编辑:我对使用无效键数组的担忧是,如果我更改界面,我不想忘记更新数组。 如果我使用数组,我需要一种类型检查的方式来确保它与接口保持一致。

一旦您接受了一个可悲的事实,即您需要维护一些与V相关的运行时工件,因为V本身将被删除,并且假设您实际上不能使用运行时工件来定义V ,那么您能做的最好的就是拥有如果您的运行时工件和V不同步,编译器会对您大喊大叫。 这是一种方法:

interface V {
    a: number;
    b: string;
}

const keysOfV = ["a", "b"] as const;
type MutuallyAssignable<T extends U, U extends V, V = T> = void;
// the following line will give an error if keysOfV is wrong
type KeysOfVIsGood = MutuallyAssignable<keyof V, typeof keysOfV[number]>; // okay

请注意keysOfV定义中的as const 这是一个const断言,需要它(或类似的东西)让编译器跟踪keysOfV的文字字符串元素,而不是推断正确但太宽的类型string[]

然后, MutuallyAssignable<T, U>是一种评估为void的类型,但我们并不真正关心它的评估结果。 我们关心的是T约束U ,并且U被约束到T (通过默认参数来回避循环约束违规)。 在某些类型XY上使用MutuallyAssignable<X, Y>时,如果编译器无法识别XY是可相互赋值的,则会出现编译器错误。

然后你可以 go 定义和使用你的hasInvalidProperties() function 但是你想要,使用keysOfV 或许是这样的:

function hasInvalidProperties(x: object): x is { [K in keyof V]: Record<K, any> }[keyof V] {
    return Object.keys(x).some(k => hasInvalidProperties.badKeySet.has(k));
}
hasInvalidProperties.badKeySet = new Set(keysOfV) as Set<string>;

/// test

function getDataFromRequest(): object {
    return Math.random() < 0.5 ? { c: "okay" } : { a: "bad" };
}
const x = getDataFromRequest();
if (hasInvalidProperties(x)) {
    console.log("not okay");
    throw new Error();
}
console.log("okay");

主要事件是当keysOfV错误时会发生什么。 以下是缺少条目时发生的情况:

const keysOfV = ["a"] as const;
type MutuallyAssignable<T extends U, U extends V, V = T> = void;
// the following line will give an error if keysOfV is wrong
type KeysOfVIsGood = MutuallyAssignable<keyof V, typeof keysOfV[number]>; // error!
// "b" is not assignable to "a" ------> ~~~~~~~

当它有一个额外的条目时会发生以下情况:

const keysOfV = ["a", "b", "c"] as const;
type MutuallyAssignable<T extends U, U extends V, V = T> = void;
// the following line will give an error if keysOfV is wrong
type KeysOfVIsGood = MutuallyAssignable<keyof V, typeof keysOfV[number]>; // error!
// "c" is not assignable to "a" | "b" ---------> ~~~~~~~~~~~~~~~~~~~~~~

希望这些错误消息和位置的描述性足以让您了解如何在V更改时修复它。


好的,希望有帮助; 祝你好运!

链接到代码

如果您愿意使用转换器,您可以在编译时获取接口上的属性列表,然后将其解析为运行时字符串列表。 因为它依赖于编译器插件(用 Typescript 编写),所以它不是原生的 TypeScript 解决方案,但它可以让您不必将数据从接口复制到字符串列表中。

请参阅此答案的详细信息。

使用 typescript 无法做到这一点,因为当您的代码运行时,您不再了解您的类型。 做到这一点的唯一方法是使用其他工具。

我们使用的一种系统是 json-schema。 我们为进出 API 的所有内容编写模式,并使用工具将 JSON-Schema 自动转换为 Typescript 类型。 我们不做相反的事情,因为 json-schema 比 typescript 更具表现力。

通过这种方式,我们使用 1 个规范源获得运行时验证和 static 类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM