使用TypeScript

时间:2019-03-01 11:55:26

标签: json typescript generics

在TypeScript中考虑以下界面

interface IApiCall<TResponse> {
    method: string;
    url: string;
}

然后在以下方法中使用哪种方法;

const call = <TResponse>(api: IApiCall<TResponse>): void => {
    // call to API via ajax call
    // on response, grab data
    // use JSON.parse(data) to convert to json object
    return json as TResponse;
};

现在,在我们的方法中将其用于类型安全,因此我们知道从API返回了哪些对象。但是,当我们从API返回单个字符串时,JSON.parse会将字符串'12345'转换为数字,然后在尝试将其视为字符串并使用{{ 1}},但它已被翻译成数字。

有解决此问题的想法,以便我们不将字符串转换为数字。

  1. 如何阻止JSON.parse将单个字符串值转换为数字?

  2. 如果使用JSON.parse,我们将检查value.trim()的类型并将其与生成的json的类型进行比较。

TResponse

但是,确定通用类型的方法似乎并不明显。

enter image description here

2 个答案:

答案 0 :(得分:2)

类型注释仅在TS中存在(TResponse在输出JS中不存在),您不能将它们用作值。您必须使用实际值的类型,此处应足以选择字符串,例如

if (typeof json == 'string')

答案 1 :(得分:2)

问题1:如何阻止JSON.parse()将单个字符串值转换为数字?

JSON是一种文本格式,因此在JSON.parse(x)中,x必须是string。但是JSON文本表示不必要的数据string。听起来您可能正在category mistake的帮助下制作confusing a thing with its representation

如果将数字12345转换为JSON(JSON.stringify(12345)),则会得到字符串"12345"。如果您解析该字符串(JSON.parse("12345")),则会返回数字12345。如果要获取字符串"12345",则需要将其编码为JSON(JSON.stringify("12345")作为字符串"\"12345\""。如果您解析那个JSON.parse('"12345"'),则会得到字符串"12345"

因此,“如何阻止JSON.parse()将单个字符串值转换为数字”这一问题的直接答案是“通过正确引用”。但是也许真正的问题是您在根本不是JSON的东西上使用JSON.parse()。如果为您提供了字符串"12345",并且想将其视为字符串"12345",那么您根本就不希望对它做任何事情……直接使用它而无需调用{ {1}}。

希望有帮助。如果由于某些原因其中之一对您不起作用,则应将您的用例的更多详细信息作为Minimal, Complete, and Verifiable example发布。


问题2:如何确定返回的JSON解析对象与通用类型匹配?

在TypeScript中,类型系统仅在设计时存在,并且在稍后运行的发出的JavaScript代码中为erased。因此,您无法在运行时访问接口并输入诸如JSON.parse()之类的参数。通用的解决方案是从运行时解决方案开始(您将如何在纯JavaScript中做到这一点)并帮助编译器在设计时推断正确的类型。

此外,接口类型TResponse

IApiCall

没有structuralinterface IApiCall<TResponse> { method: string; url: string; } 的{​​{3}}依赖性。因此,即使我们编写了良好的运行时代码并尝试从中推断类型,编译器也将永远无法找出TResponse是什么。

在这种情况下,我建议您使TResponse接口包含一个not recommended成员,然后您必须为自己关心的每种类型编写自己的运行时测试。像这样:

IApiCall

下面是一个如何为特定的interface IApiCall<TResponse> { method: string; url: string; validate: (x: any) => x is TResponse; } 类型创建此类事物的示例:

TResponse

您会发现interface Person { name: string, age: number; } const personApiCall: IApiCall<Person> = { method: "GET", url: "https://example.com/personGrabber", validate(x): x is Person { return (typeof x === "object") && ("name" in x) && (typeof x.name === "string") && ("age" in x) && (typeof x.age === "number"); } } 应该是运行时的良好检查,以检查personApiCall.validate(x)是否与x界面匹配。然后,您的Person函数可以实现如下:

call()

请注意,const call = <TResponse>(api: IApiCall<TResponse>): Promise<TResponse | undefined> => { return fetch(api.url, { method: api.method }). then(r => r.json()). then(data => api.validate(data) ? data : undefined); }; 返回一个call(api调用可能是异步的,对吗?如果验证失败,Promise<Person | undefined>将返回一些信息……您可以抛出异常如果你想)。现在您可以undefined,编译器将自动了解异步结果是call(personApiCall)

Person | undefined

好的,我希望这些答案能给您一些指导。祝你好运!