繁体   English   中英

如何使用 Typescript 中的通用索引来处理映射类型?

[英]How to address a mapped type with a generic index in Typescript?

我正在尝试使用泛型类型从映射类型访问属性。 但我在下面的评论中得到了错误。 输入这样的内容的正确方法是什么?

type ThingType = 'typeA' | 'typeB';

interface Thing<T extends ThingType> {
    type: T,
    name: string
};

type Container<T extends ThingType> = {
    [id: string]: Thing<T>
}

type Store<T extends ThingType = ThingType> = {
    [K in T]?: Container<K>
};

const myStore: Store = {
    'typeA': {
        'one': {type: 'typeA', name: 'one'},
        'two': {type: 'typeA', name: 'two'}
    }
};

// This one does not fail
const typeAContainer: Container<'typeA'> | undefined = myStore['typeA'];

function storeThing<T extends ThingType>(thing: Thing<T>) {
    // Error here:
    const container: Container<T> | undefined = myStore[thing.type];
    // Type 'Store<ThingType>[T]' is not assignable to type 'Container<T> | undefined'.
    //  Type 'Container<"typeA"> | Container<"typeB"> | undefined' is not assignable to type 'Container<T> | undefined'.
    //    Type 'Container<"typeA">' is not assignable to type 'Container<T>'.
    //      Type '"typeA"' is not assignable to type 'T'.
    //        '"typeA"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'ThingType'.
    //          Type 'Store<ThingType>[T]' is not assignable to type 'Container<T>'.
    //            Type 'Container<"typeA"> | Container<"typeB"> | undefined' is not assignable to type 'Container<T>'.
    //              Type 'undefined' is not assignable to type 'Container<T>'.

    // ... code here ...
}

storeThing({type: 'typeA', name: 'three'});

编译器并没有真正执行大量更高级别的分析来确认依赖于尚未指定的泛型类型参数的类型的可分配性。 storeThing的实现中,您将Store<ThingType>[T]类型的值分配给Container<T> | undefined类型的变量。 Container<T> | undefined ,其中T尚未指定为具体类型。 不幸的是,编译器在不指定T的情况下无法将它们视为兼容,即使它们与T的所有可能缩小兼容。

这本质上是 TypeScript 的设计限制。 有关此类类似问题,请参阅microsoft/TypeScript#36737microsoft/TypeScript#36349 在这两种情况下,编译器都不能遵循泛型索引访问和另一个兼容类型之间的高阶关系。 有一个现有的开放建议microsoft/TypeScript#33014可以更好地处理此类情况,但尚不清楚是否会在那里实施任何事情。

除非这种情况发生,否则我们必须想出一些前进的方向。


在这里进行的合理方法:一旦你完全确信自己正在做的事情是完全安全的(并且要小心;很容易出错并认为某事不安全),明智地使用类型断言是合适的:

const container = myStore[thing.type] as Container<T> | undefined; // okay

这类事情是类型断言的预期用例:您对类型的了解比编译器可以验证的更多的情况。 你只是断言你正在做的事情是安全的,然后继续前进。 当然,存在一些危险,即您对编译器撒了谎……或者您的代码将来会更改并将先前正确的类型断言变成谎言。 类型断言将验证类型安全的负担从编译器转移到了开发者身上,只要你愿意承担这个责任就可以了。


另一种继续进行的方法:找到一些自包含的类型操作,即使使用未指定的泛型,编译器也能遵循您的推理。 这是一门艺术,并不总是可能的。 这里我要做的是这个两步过程:

const myStoreNarrowed: Store<T> = myStore; // okay
const container: Container<T> | undefined = myStoreNarrowed[thing.type]; // okay

编译器能够识别将myStore分配给类型为Store<T>的变量是安全的,该类型比Store<ThingType>窄。 然后,它能够识别出,当您使用T键对Store<T>进行索引时,您会得到可分配给Container<T> | undefined的东西。 Container<T> | undefined

我可能会使用这种方法,因为它仍然为您提供了一些类型安全保证,而这些保证在您使用类型断言时会丢失。 但是,如果所有其他方法都失败了,那么总会有一个(经过深思熟虑的)类型断言。


好的,希望有帮助; 祝你好运!

Playground 代码链接


暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM