打字稿:类型系统不能证明看似明显正确的东西

时间:2017-12-21 14:18:54

标签: typescript

以下是getWrapper中的编译错误:

type Wrapper<K> = {
    value: K
}

type Wrappers = {
    [K in 'henk' | 'piet']: Wrapper<K>
}
const wrappers: Wrappers = {
    'henk': { value: 'henk' },
    'piet': { value: 'piet' }
}

function getWrapper<K extends keyof Wrappers>(k: K): Wrapper<K> {
    return wrappers[k]
}

它表示wrappers[k]Wrapper<'henk'> | Wrapper<'piet'>的数据。它应该能够确定wrappers[k]实际上是Wrapper<K>。我可以帮助打字稿来解决这个问题吗?

1 个答案:

答案 0 :(得分:4)

更新:2019-05-30 TypeScript 3.5的发布引入了smarter union type checking,但它没有解决这个问题,可能是因为它使用泛型类型K而不是已知具体类型的联合。因此,截至目前,以下解决方案没有任何变化。

我非常确定您的问题是TypeScript doesn't reduce unions您期望的方式。例如,您可能希望以下工作:

declare const innerUnion: { foo: string | number };
const outerUnion: { foo: string } | { foo: number } = iU; // error

但它没有。 TypeScript没有积极地将outerUnion的类型减少为innerUnion的类型。编译器没有自动执行此操作的原因似乎是实用主义:在许多情况下,这样的减少是错误的,因为还有另一个属性无法合并:

declare const iU: { foo: string | number, bar: string | number };
const oU: { foo: string, bar: string } | { foo: number, bar: string } = iU; // error

上述错误很好,因为iU可能是{ foo: 0, bar: '' }

TypeScript认为K可以是'henk' | 'piet'类型,因此函数的输出可以是Wrapper<'henk' | 'piet'>,但返回值的类型为Wrapper<'henk'> | Wrapper<'piet'>。由于上面的联合减少是TypeScript没有做到的,它表示存在错误。

那么,为什么返回值的类型为Wrapper<'henk'> | Wrapper<'piet'>?因为使用键联合的对象索引会导致该对象的属性值的并集。

这表示您的问题的解决方案,使用lookup type来表示索引操作:

function getWrapper<K extends keyof Wrappers>(k: K): Wrappers[K] {
    return wrappers[k]; // okay
}

我想,这就是你想做的。希望有所帮助;祝你好运!