James Middleton
James Middleton

Reputation: 709

Confusing Typescript Generic Typing Error

I'm really confused here. This is also an extension of this if you need any extra detail.

This is my code:

interface Collections extends Twitter.Collections, Coin.Collections {}
type CollectionName = keyof Collections
type CollectionType <T extends CollectionName> = Collections[T]

const _collections: {
    [K in CollectionName]?: Collection<CollectionType<K>>
} = {}

export async function getCollection<T extends CollectionName> (name: T): Promise<Collection<CollectionType<T>>> {
    if (name in _collections && typeof _collections[name] !== 'undefined') {
        return _collections[name]
    }

    ...
}

It's on this last if line that the following ts error displays:

Type '{ "twitter:tweets"?: Collection<Tweet> | undefined; "twitter:users"?: Collection<User> | undefined; "twitter:metadata-cashtag"?: Collection<CashtagMetadataDb> | undefined; "coins:all"?: Collection<...> | undefined; }[T]' is not assignable to type 'Collection<Collections[T]>'.
    Type 'Collection<Tweet> | Collection<User> | Collection<CashtagMetadataDb> | Collection<Coin> | undefined' is not assignable to type 'Collection<Collections[T]>'.
        Type 'undefined' is not assignable to type 'Collection<Collections[T]>'.

As you can see, I've tried my best to type check here but I'm unable to get this to work.

Thanks for your help :)

Upvotes: 0

Views: 72

Answers (1)

Matt McCutchen
Matt McCutchen

Reputation: 30999

Automatic narrowing doesn't work on element access expressions like _collections[name]. If you want to take advantage of narrowing, you'll have to save the value in a local variable before testing it. And due to limitations on how TypeScript reasons about lookup types, you'll need a type annotation on that local variable. This should work:

export async function getCollection<T extends CollectionName>(name: T): Promise<Collection<CollectionType<T>>> {
    let coll: Collection<CollectionType<T>> | undefined = _collections[name];
    if (coll !== undefined) {
        return coll
    }
    // ...
}

Upvotes: 1

Related Questions