这是Typescript: passing interface as parameter for a function that expects a JSON type的扩展(询问如何将接口传递给JSON类型的函数),而Typescript: passing interface as parameter for a function that expects a JSON type又是Typescript: interface that extends a JSON type的扩展(询问如何在JSON类型之间转换)
这些问题与JSON Typescript类型相关:
type JSONValue =
| string
| number
| boolean
| null
| JSONValue[]
| {[key: string]: JSONValue}
在Typescript: passing interface as parameter for a function that expects a JSON type中,最终的答案表明不可能将接口传递给需要JSON值的函数。
interface Foo {
name: 'FOO',
fooProp: string
}
const bar = (foo: Foo) => { return foo }
const wrap = <T extends JSONValue[]>(
fn: (...args: T) => JSONValue,
...args: T
) => {
return fn(...args);
}
wrap(bar, { name: 'FOO', fooProp: 'hello'});
失败,因为接口Foo
无法指定给JSONValue
,即使通过分析很容易识别出转换应该很好。
请参见Playground以及https://github.com/microsoft/TypeScript/issues/15300
先前的答复指出:
我们在不扩展JSONValue类型的情况下的唯一解决方法是将[interface] Foo转换为一个类型。
在我的例子中,我可以修改JSONValue类型,但是不能轻易地修改所有相关的接口。
2条答案
按热度按时间bbmckpt71#
我最初的意思是放松
JSONValue
类型,您可以满足于object
类型。但是,您实际上失去了类型安全,因为函数现在接受应该无效的类型,如
它有一个函数类型的属性
fn
。理想情况下,我们不允许将此类型传递给函数。但不是所有的希望都破灭了我们还有一个选择将类型 * 推断 * 为泛型类型并递归验证它。
ValidateJSON
接受某个类型T
并遍历其类型。它检查该类型的属性,如果该类型无效,则将其解析为never
。我们可以使用这个实用程序类型来验证
wrap
中fn
的参数类型和返回类型。所有这些都会导致以下行为:
Playground
yduiuuwa2#
如果您使用一个单独的泛型作为返回类型(它也扩展了
JsonValue
),那么您就不会有类型兼容性问题,并且您还可以推断出一个更窄的返回类型:在您显示的
JsonValue
类型上:对于具有string
键和JsonValue
值的联合成员(对象类型):更正确的做法是将undefined
包含在与JsonValue
的并集中,使键有效地可选......因为对象中的每个键永远不会有值,并且访问对象上不存在的键所产生的值在运行时是undefined
:这与JavaScript中
JSON
对象的serialization和deserialization算法兼容,因为它既不序列化以undefined
作为值的属性,也不支持以undefined
作为类型,因此undefined
永远不会存在于反序列化的值中。这种类型既方便(您可以对任何属性名使用点标记属性访问),又是类型安全的(您必须将每个值限制为
JsonValue
才能安全地使用它)。TS Playground中的完整代码
根据您的意见更新:
操场上的答案仍然依赖于接口Foo是一个类型。2但是我需要它是一个接口。3这可能吗?
只有当接口扩展
JsonValue
的类对象联合成员(受其约束)时,才有可能。TS手册的类型别名和接口之间的区别一节以下面的信息开始(重点是我):
类型别名和接口非常相似,在很多情况下你可以自由选择它们。
interface
的几乎所有特性在type
中都可用,关键区别在于类型不能重新打开以添加新属性,而接口总是可扩展的。这意味着每个类型别名都是在定义它的地方完成的......但是接口总是可以扩展(变异),所以直到类型检查发生之前,确切的形状实际上还没有完成,因为其他代码(例如使用您的代码的其他代码)可以更改它。
防止这种情况发生的唯一方法是限制相关接口允许使用哪些扩展:
下面是一个使用原始的无约束接口的示例,其中显示了编译器错误:
但是,如果接口被约束为只允许与
JsonValue
的类对象联合成员兼容,则不存在潜在的类型兼容性问题:TSPlayground代码
另请参见TS手册中的utility types