使用Typescript遇到意外的事情
我一直在使用以下模式来指定对象是string
个键到Foo
值的字典
type Dictionary = { [id: string]: Foo }
真正奇怪的是,当我尝试使用 any 键从Dictionary中检索值时,Typescript给出的返回类型为Foo
我希望它是Foo | undefined
,因为如果键未映射到任何值,当然会返回undefined
。
这是为什么这种行为异常的示例-see the playground here
type Foo = { bar: number }
type Dictionary = { [id: string]: Foo }
const a: Dictionary = {
one: { bar: 1 },
two: { bar: 2 }
}
const addOneToBar = (foo: Foo) => foo.bar + 1
// No type error, maybe expected, because we 'know' from context that
// the one property exists on the Dictionary instance a
alert(addOneToBar(a.one))
alert(addOneToBar(a['one']))
try {
// There is still no type error here, though. Why?
// Shouldn't a.three / a['three'] return Foo | undefined
// instead of Foo, because we're not certain there is any three property?
alert(addOneToBar(a.three))
alert(addOneToBar(a['three']))
} catch (err) {
alert('Of course, there is a null pointer error: ' + err)
}
在我看来,这段代码显然不是类型安全的,确实会导致运行时异常,而该异常不会被Typescript标记。
我对此感到非常惊讶-返回undefined
指向什么都没有的键是Javascript对象的基本行为,而且我猜想大多数语言都可以使用任何通用的字典/地图实现。我不明白为什么Typescript会错过这个。
也许我在上面的代码中犯了一个错误,或者我误解了{ [id: string]: Foo }
模式的目的,并且/或者还有另一种用于在Typescript中键入字典的模式,它确实具有这种行为,请改用(我已经尝试过Record
类型,再次令我惊讶的是,它也表现出相同的行为)。
这是怎么回事?
修改
我刚刚意识到写了一个问题,我可能可以type Dictionary = { [id: string]: Foo | undefined }
来获得这种行为。但是,为什么这不是默认值呢?也许如果您有一个狭窄的键类型(如枚举),这是有道理的,但是如果您有一个string
键,则保证始终从字典返回值是绝对错误的,不是吗?
进一步编辑
就像在评论中一样,我现在已经意识到,以这种方式行事的Typescript实际上是有道理的,并且上述解决方案明确告诉TS字典可能返回未定义的值就是解决方案。从某种意义上讲,这是我假设TS应该从类型推断事物,而不是我必须明确声明它们-您可以以任何一种方式争论这种情况-我建议它应该表现的方式对于可能会丢失键的情况是有益的,但是当您确定它们不会出现时,这很不利。 TS实际在此处的行为方式则相反。
Mods
这不是一个答案本身的问题(TypeScript只是按照设计的方式工作),但是除非得到密切的投票,否则我将把它保留下来,因为我认为这是有关该行为的有趣总结,为什么是按原样实现的。
答案 0 :(得分:0)
通过声明doOnLayout
,您是告诉编译器任何字符串都是此类型的有效键。然后,编译器将不假设哪些字符串是有效键,哪些不是,这实际上是在定义任何属性访问器,并且将返回type Dictionary = { [id: string]: Foo }
。
如果要强制只为对象定义Foo
和one
键,则可以严格定义字典的属性:
two