键入带有可选键的对象,但存在的键的非空值

时间:2019-01-30 17:23:21

标签: typescript

我正在使用strict: true使用TypeScript 3.2.2。

我一直无法输入具有一组动态定义键的普通对象,但是所有这些键都有保证的非空值类型。

[TypeScript playground example]

// Will assume any property access has type T
interface Object<T> {
    [key: string]: T;
}

// Will assume Object.values() could be undefined
interface Object<T> {
    [key: string]: T | undefined;
}


// Doesn't compile
interface Object<T> {
    [key: string]?: T;
}

2 个答案:

答案 0 :(得分:1)

您可以使用未知类型:

interface Object {
    [key: string]: unknown;
}

来自TypeScript文档:

  

[...] unknown是任何类型安全的副本。任何东西都可以分配给未知对象,但未知对象不能分配给任何东西,只有它本身以及没有类型声明或基于控制流的缩小。同样,在未声明或缩小为更具体的类型之前,不允许对未知数进行任何操作。

答案 1 :(得分:1)

如评论中所述,TypeScript在distinguishing missing properties from undefined properties方面做得不好。这至少在某种程度上是合理的,因为当您从JavaScript中的对象中读取缺少的属性时,您将得到undefined(而不是错误)。在TypeScript 2.0中引入--strictNullChecks compiler option之前,TypeScript在区分丢失的/未定义的属性与当前的/定义的属性方面做得很糟糕。 --strictNullChecks主要解决了此问题,但仍有一些问题悬而未决。

此问题仍然显示在types with index signatures中。索引签名仅被视为存在。无法说出存在某些键,而某些键不带索引签名。您需要的optional index signature语法不是该语言的一部分。启用--strictNullChecks时,签名{[k: string]: SomeType}表示每个单个字符串键属性都存在,并且类型为SomeType,但是在向其分配对象时实际上并没有强制执行此操作。使用索引签名的唯一真正安全的类型方法是将| undefined添加到值类型,例如{[k: string]: SomeType | undefined},但这在很多情况下最终变得非常烦人,导致人们过度使用非null断言,doesn't help anyone。事实就是这样。

现在,您的选择就如我所见:

  • 使用不带| undefined的索引签名,并处理以下事实:编译器认为所有属性都存在,而某些属性不存在(请检查编译器认为没有必要,例如{{1 }})

  • 使用带有if (unknownKey in foo) {... foo[unknownKey] ...}的索引签名,并处理以下事实:编译器不断警告您,如果知道某个值,则可能未定义该值(请执行其他不需要的检查,例如| undefined;或类似const prop = foo[knownKey]; if (typeof prop !== 'undefined') { ... prop ... }的非空断言

  • 不使用索引签名。您可能可以避免使用mapped type,其中的键集是一些您在定义类型时不知道但在使用时知道的文字的并集。也就是说,使用预定义的Record<K, T>类型:

    const prop = foo[knownKey]!

    但是在尝试操作时会变得更加复杂,因此在这里我不会深入探讨。

不确定要说些什么。由于替代方案似乎更令人讨厌,因此语言限制一直存在。如果您有一个引人注目的用例,则可以在某些链接的GitHub问题上发表评论或打开一个新的问题,但我预计此处不会有太大变化。

哦,希望对您有所帮助。祝你好运!