typescript - 如何在 Typescript 中访问可能的空对象的对象属性?
问题描述
我有 2 个类似的设置,我希望它们产生相同的结果,但第二个会引发 Typescript 错误:
第一的:
interface ISample {
requiredProp: string
optionalProp?: string
}
const sampleObj: ISample = { requiredProp: "hello" }
const { requiredProp, optionalProp = "default" } = sampleObj
console.log(requiredProp, optionalProp)
// Expected Output: "hello", "default"
// Actual Output: "hello", "default"
第二:
interface ISample {
requiredProp: string
optionalProp?: string
}
const sampleObj: ISample | {} = { requiredProp: "hello" } // object can now be empty
const { requiredProp = "default", optionalProp = "default" } = sampleObj
console.log(requiredProp, optionalProp)
// Expected Output: "hello", "default"
// Actual Output: Error: Property 'requiredProp' does not exist on type '{} | ISample'. Property 'optionalProp' does not exist on type '{} | ISample'.
是的,谢谢打字稿我知道这些道具可能不存在于那个空对象上,这就是为什么我立即在同一行上为它们分配默认值...
我需要使对象可选地为空,并且仍然能够从中解构未定义的值,因此我可以为它们分配默认值(如上面的示例中所示)或将它们与可选的链接运算符一起使用。如何在不键入任何对象的情况下摆脱这个无用的错误?
解决方案
简而言之,您无法访问ISample
属性,sampleObj
因为 TypeScript 无法推断性{}
地区分联合成员,ISample | {}
因为两个联合成员都对分配感到满意。因此,您不能访问一个属性,sampleObj
除非它存在于联合的所有成员上(排除所有属性)。
工会
来自手册:
联合类型是由两种或多种其他类型组成的类型,表示可能是这些类型中的任何一种的值。我们将这些类型中的每一种都称为工会的成员。
请记住,联合不组合类型(这是交集的作用);它们为您提供了一个事物(变量、参数、属性等)在 assignment中可能存在的选项。例如:
// foo, by annotation, might be a string or a number...
let foo: string | number = 'bar';
// ... but it is assigned to a string, so its realized type is string:
foo.toUpperCase(); // ok, because foo: string
// We can reassign foo as long as it is still a member of the original annotated union
foo = 0;
foo.toUpperCase(); // error, because foo: number
// ^^^^^^^^^^^ Property 'toUpperCase' does not exist on type 'number'
这里基于分配foo
推断出更窄的类型(或者更确切地说,工会成员string
被歧视)。
再次强调,歧视工会成员(在分配或其他情况下)不允许这些成员,直到重新分配,即你不能这样做:
let foo: { bar: string } | { baz: number } = { bar: 'foobar' }
foo.baz = 0
// ^^^ Property 'baz' does not exist on type '{ bar: string; }'.
foo = { baz: 0 } // ok
空的类型{}
...空类型似乎违背预期:
class Empty {} function fn(arg: Empty) { // do something? } // No error, but this isn't an 'Empty' ? fn({ k: 10 });
考虑以下有效的 TypeScript:
let foo: {} = {};
foo = { bar: 0 };
foo = 'bar';
foo = [ true, 0, '' ];
这些都是有效的,因为每个属性{}
都存在于这些实际类型中的每一个上。如果我们改变它:
let foo: { length: number } = { length: 0 };
const o = { bar: 0 }
foo = o;
//^^^ Property 'length' is missing in type '{ bar: number; }'
// but required in type '{ length: number; }'
foo = 'bar';
foo = [ true, 0, '' ];
在这里,除了其中一个之外,所有这些都满足length: number
。{}
没有要满足的属性,所以任何事情都会发生(除了null
and undefined
)。
另请注意,如果{}
没有类型断言,该类型实际上是无用的:
let foo: {} = 'bar';
foo.length;
// ^^^^^^ Property 'length' does not exist on type '{}'
结论/答案
因此,再次查看发布的示例:
interface ISample {
requiredProp: string
optionalProp?: string
}
const sampleObj: ISample | {} = { requiredProp: "hello" } // object can now be empty
const { requiredProp = "default", optionalProp = "default" } = sampleObj
// ^^^^^^^^^^^^ ^^^^^^^^^^^^
// Property 'requiredProp' does not exist on type '{} | ISample'
// Property 'optionalProp' does not exist on type '{} | ISample'.
console.log(requiredProp, optionalProp)
在分配 的初始值时sampleObj
,TypeScript 尝试通过区分与给定值不匹配的成员来缩小类型。在这种情况下,它不能排除任何工会成员,因为{ requiredProp: "hello" }
同时满足ISample
和{}
。
为了彻底起见,请注意const { requiredProp = "default", optionalProp = "default" } = sampleObj
正在尝试访问sampleObj.requiredProp
and sampleObj.optionalProp
,它不能存在于 type 上{} | ISample
。您的选择是:
- 更改
ISample
,以便它可以允许一个空对象(通过使所有属性可选)。 - 更改
sampleObj
要使用的类型,在不更改定义Partial<ISample>
的情况下完成相同的事情。ISample
- 始终将键入的事物的所需属性分配
ISample
给默认值。
无论您的选择如何,您都应该{}
从联合中删除以避免头痛(尽管您可能会查看类型谓词)。
推荐阅读
- php - 将 PHP 8 添加到 wamp 后,phpmyadmin 无法正常工作
- c# - 在 WPF C# 中扩展 TextBox 以使用 MaxLength
- azure - Azure 服务总线 - 废弃的消息返回到同一订阅者
- html - 溢出-y 滚动具有 100vh 的固定元素的特定子元素而不显示滚动条?
- python-3.x - Python Tkinter:为列表框中的每个字符串创建一个变量?
- node.js - 运行“firebase deploy --only 功能”时出错
- neo4j - 为什么 neo4j EXPLAIN 显示数千行带有唯一的过滤器 DISTINCT 节点?
- rest - 如何从 WCF-REST API 服务创建 Swagger 规范
- image - 如何从目录中获取 Tensorflow 数据集对象的列表,每个对象都包含来自一个 ID 的图像?
- python - 我收到此错误 IndexError: too many indices for array