typescript - 使用类型保护缩小函数返回类型
问题描述
我有一个 TypeScript 函数,它解析一些 JSON 并通过类型保护运行它以确保数据有效,以便编译时代码的其余部分知道它正在处理一个实际遵守预期接口的对象。
但是,我很难让 TypeScript 强制执行类型保护。显然JSON.parse
返回any
,它可以分配给任何其他类型,因此会检查,即使我指定了非any
返回类型。
const validPerson = `{"firstName": "John", "lastName": "Doe"}`;
const invalidPerson = `{"foo": 123}`;
interface Person {
firstName: string;
lastName: string;
}
interface PersonGetter {
(json: string): Person | undefined;
}
function isPerson(o: any): o is Person {
return typeof o.firstName === "string" && typeof o.lastName === "string";
}
// BAD: Type checks, but it's overly permissive. `JSON.parse` could return anything.
const getPerson1: PersonGetter = (json) => {
const o = JSON.parse(json);
return o;
}
// GOOD (kinda): Requires type guard to pass.
// `unknown` requires TS 3, which is fine in general, but bad for me.
// Also, I feel like having to remember to case the return from `JSON.parse` is a responsibility the programmer shouldn't bear.
const getPerson2: PersonGetter = (json) => {
const o: unknown = JSON.parse(json);
if (isPerson(o)) {
return o;
} else {
return undefined;
}
}
// GOOD (kinda): Requires type guard to pass. Works in TS 2.8.
// Still, not great that I have to cast the return value from `JSON.parse`, but I could probably work around that.
type JSONPrimitive = string | number | boolean | null;
type JSONValue = JSONPrimitive | JSONObject | JSONArray;
type JSONObject = { [member: string]: JSONValue };
interface JSONArray extends Array<JSONValue> {}
const getPerson3: PersonGetter = (json) => {
const o: JSONValue = JSON.parse(json);
if (isPerson(o)) {
return o;
} else {
return undefined;
}
}
选项 3 对我有用,但它使用的建议 JSON 类型仍有争议,并且仍然将责任放在实现者身上(他们可以很容易地根本不使用类型保护,并且仍然认为他们遵守接口)。
看来JSON.parse
返回any
是我的问题的根源。我已经在strict
模式下运行,但它似乎仍然允许将显式键入的内容any
扩展为函数的显式返回类型。
有没有另一种方法告诉 TypeScript 函数的返回值必须是它实现的接口中指定的返回类型,而不是any
?
解决方案
JSON 在 lib.es5.d.ts 中声明。在您的项目中创建您自己的类型定义文件,并声明一个新的全局 JSON 实例,其定义返回一个虚拟类型,parse()
而不是any
.
这样,您将不得不使用保护或强制转换结果,以避免在具有显式定义的返回类型的函数和方法中出现编译错误。
interface JSONStrict extends JSON {
/**
* Converts a JavaScript Object Notation (JSON) string into an object.
* @param text A valid JSON string.
* @param reviver A function that transforms the results.
* This function is called for each member of the object.
* If a member contains nested objects, the nested objects are
* transformed before the parent object is.
*/
parse(text: string, reviver?: (key: any, value: any) => any): { _dummyProp?: void };
}
// overide lib.es5 declaration of JSON
declare const JSON: JSONStrict;
/* ... */
function parseAndThrowCompilationError(): Person {
var result = JSON.parse('{ "x": 1}');
return result;
// Type '{ _dummyProp?: void }' has no properties in common with type 'Person'
}
我添加_dummyProp
到结果中是因为仅使用对象将匹配仅具有可选属性的接口并且不会引发错误。
...老实说,这有点像钻机,我想知道这种努力是否真的值得。
推荐阅读
- macos - OSX - 限制对菜单项的访问
- heroku - Shopify 脚本标签:302 CORB 错误,脚本标签重定向到身份验证流程
- cookies - a.abc.com 和 b.abc.com 之间的第一方 Cookie
- linux - Jenkins 在远程 ssh 上执行带参数的 shell 脚本
- javascript - 如何在滚动时增加 div 高度/paddint-top 并检测到 div 的图像冲突?
- python - 在 Anaconda 中升级到 scipy 1.7?
- javascript - 在按钮单击时播放 CSS 动画将不起作用
- c++ - 检查 std::vector 是否
包含作为 const char * 给出的字符序列 - sql - postgresql对用户id的动态引用
- tensorflow - XLNet:命名实体识别的自定义训练 - Huggingface Transformers - Tensorflow