简体   繁体   English

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

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

I'm trying to access a property from a mapped type using a generic type.我正在尝试使用泛型类型从映射类型访问属性。 But I'm getting the error in the comment bellow.但我在下面的评论中得到了错误。 What it the correct way to type something like this?输入这样的内容的正确方法是什么?

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'});

The compiler doesn't really perform a whole lot of higher-level analysis required to confirm the assignability of types that depend on as-yet-unspecified generic type parameters.编译器并没有真正执行大量更高级别的分析来确认依赖于尚未指定的泛型类型参数的类型的可分配性。 Inside the implementation of storeThing , you are assigning a value of type Store<ThingType>[T] to a variable of type Container<T> | undefinedstoreThing的实现中,您将Store<ThingType>[T]类型的值分配给Container<T> | undefined类型的变量。 Container<T> | undefined , where T has not yet been specified as a concrete type. Container<T> | undefined ,其中T尚未指定为具体类型。 And unfortunately the compiler cannot see those as compatible without specifying T , even though they are compatible for all possible narrowings of T .不幸的是,编译器在不指定T的情况下无法将它们视为兼容,即使它们与T的所有可能缩小兼容。

This is essentially a design limitation of TypeScript.这本质上是 TypeScript 的设计限制。 See microsoft/TypeScript#36737 and microsoft/TypeScript#36349 for similar issues of this sort;有关此类类似问题,请参阅microsoft/TypeScript#36737microsoft/TypeScript#36349 in both cases, the compiler cannot follow the higher-order relationship between a generic indexed access and another compatible type.在这两种情况下,编译器都不能遵循泛型索引访问和另一个兼容类型之间的高阶关系。 There is an existing open suggestion, microsoft/TypeScript#33014 , to handle cases like this better, but it's not clear if anything is going to be implemented there.有一个现有的开放建议microsoft/TypeScript#33014可以更好地处理此类情况,但尚不清楚是否会在那里实施任何事情。

Until and unless that happens, we have to come up with some way forward.除非这种情况发生,否则我们必须想出一些前进的方向。


A reasonable way to proceed here: once you have well and truly convinced yourself that what you are doing is perfectly safe (and be careful; it's easy to get this wrong and think that something is safe when it's not), a judicious use of a type assertion is appropriate:在这里进行的合理方法:一旦你完全确信自己正在做的事情是完全安全的(并且要小心;很容易出错并认为某事不安全),明智地使用类型断言是合适的:

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

This sort of thing is the intended use case for type assertions: situations in which you know more about the type than the compiler can verify.这类事情是类型断言的预期用例:您对类型的了解比编译器可以验证的更多的情况。 You just assert that what you're doing is safe, and move on.你只是断言你正在做的事情是安全的,然后继续前进。 There's some danger, of course, that you have lied to the compiler... or that your code will change in the future and turn the previously correct type assertion into a lie.当然,存在一些危险,即您对编译器撒了谎……或者您的代码将来会更改并将先前正确的类型断言变成谎言。 Type assertions shift the burden of verifying type safety from the compiler to the developer, which is fine as long as you're willing to accept that responsibility.类型断言将验证类型安全的负担从编译器转移到了开发者身上,只要你愿意承担这个责任就可以了。


Another way to proceed: find some type manipulations which are self-contained enough for the compiler to follow your reasoning even with an unspecified generic.另一种继续进行的方法:找到一些自包含的类型操作,即使使用未指定的泛型,编译器也能遵循您的推理。 This is a bit of an art, and it's not always possible.这是一门艺术,并不总是可能的。 Here what I would do is this two-step process:这里我要做的是这个两步过程:

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

The compiler is able to recognize that it's safe to assign myStore to a variable of type Store<T> , which is narrower than Store<ThingType> .编译器能够识别将myStore分配给类型为Store<T>的变量是安全的,该类型比Store<ThingType>窄。 And then, it's able to recognize that when you index into Store<T> with a T key you get something assignable to Container<T> | undefined然后,它能够识别出,当您使用T键对Store<T>进行索引时,您会得到可分配给Container<T> | undefined的东西。 Container<T> | undefined . Container<T> | undefined

I'd probably use this approach since it still gives you some type safety guarantees that are lost when you use a type assertion.我可能会使用这种方法,因为它仍然为您提供了一些类型安全保证,而这些保证在您使用类型断言时会丢失。 But if all else fails, there's always a (carefully thought out) type assertion.但是,如果所有其他方法都失败了,那么总会有一个(经过深思熟虑的)类型断言。


Okay, hope that helps;好的,希望有帮助; good luck!祝你好运!

Playground link to code Playground 代码链接


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

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