Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inferred generic type gets lost when trying to return value #36737

Closed
LinusU opened this issue Feb 11, 2020 · 5 comments
Closed

Inferred generic type gets lost when trying to return value #36737

LinusU opened this issue Feb 11, 2020 · 5 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@LinusU
Copy link
Contributor

LinusU commented Feb 11, 2020

(sorry for the bad title, I really didn't know how to describe this at all, and I've tried reaching out to some other developers who hade no idea either 😬 For the same reason I've also had a hard time searching for similar issues, and didn't really find anything)

TypeScript Version: 3.8.0-dev.20200208 (and 3.7.5)

Search Terms: "is not assignable to"

Code

interface Base { _id: string }
interface Car extends Base { make: string }
interface Pet extends Base { name: string }

interface Collection<T> {
    find(filter: Partial<T>): Promise<T[]>
}

declare const collections: {
    car: Collection<Car>
    pet: Collection<Pet>
}

type ExtractCollection<T extends keyof typeof collections> = (typeof collections)[T] extends Collection<infer R> ? R : never



// $ExpectType Promise<Pet[]>
let a = collections.pet.find({ name: 'test' })

// $ExpectType Car
let b: ExtractCollection<'car'>

// $ExpectType Car | Pet
let c: ExtractCollection<'car' | 'pet'>

// $ExpectType Promise<Car[]>
let d = findById('car', 'test')



function findById<T extends keyof typeof collections>(name: T, id: string): Promise<Array<ExtractCollection<T>>> {
    return collections[name].find({ _id: id }) // <------ Type 'Pet' is not assignable to type 'ExtractCollection<T>'
}

Expected behavior:

I expected no error since ExtractCollection<'pet'> resolves to Pet.

Actual behavior:

Type Pet is not assignable to type ExtractCollection<T>

Playground Link: http://www.typescriptlang.org/play/?ts=3.8.0-dev.20200208&ssl=1&ssc=1&pln=33&pc=1#code/JYOwLgpgTgZghgYwgAgEJwM4oN7IPrAAmAXMhmFKAObIC+AUKJLIigMJxTIQAekIhDGkw5kAWzgBrCKXKUQNBk2jwkyAAoQw3PhAFD0WZLhBwxMshWp16jcCtbI2AewA2riAjDBnIADwAKgB8xvTI4cgwoIQAFFGuzKTqnN5wroFBAJRJUM5iwFiBANoAukH0DPSEnq6cKAi+5MgN7p7ejaTYYREInKQurV4+-hxQ5RHIAA5a-W4eQ75+mmDllWAAntPIAKJ8UIhgA-Pt-gE6-ILI0uvOMMgb07fNc23DGCEAvMgxDxBPLcc3pkigESuc9Jcjq9FqAYNBkAAlEIAfkRyFIIAgADdoLZbAB6fHIAAku2mXgCmxQ6ly+UKy1K5Q82jgyC+AOhIAwADppmBuVEBDETGYLABySDkMV0TIEomknjksCUrajejM5AAI1IuwoByhC38Yt6UDF5XohJJZLaKvYnGQAB8NFp1Vpmjq9vqXoa-MbONKnWK+Wa5VbFTaqRpaQUIH5RozXdpCGzItFUOsAJKxP2mgA0yAlEClsvoMAAriBDamBOms4FwforhAbndfv9vSd3jFTOZSAF80RZFYFNko3kY34AIJQfbrPy6-ZeA0nDJBEJdCZQLRlqAgZ6DTtFHsQEoC6LC-CD5BEGUVehAA

Related Issues: no 😢

@jcalz
Copy link
Contributor

jcalz commented Feb 11, 2020

The compiler's not really adept at verifying that values are assignable to types that depend on as-yet-unspecified generic type parameters, such as the T inside the implementation of findById(). You're going to want to use a type assertion or similar type-safety-loosening technique to get that to compile with no error, as in:

function findById<T extends keyof typeof collections>(name: T, id: string) {
    return collections[name].find({ _id: id }) as Promise<Array<ExtractCollection<T>>>;
}

The closest I can imagine to getting the compiler to verify the types is to represent what you're doing as an indexing operation, like this:

function findById<T extends keyof typeof collections>(name: T, id: string) {
    return {
        get car() { return collections.car.find({ _id: id }) },
        get pet() { return collections.pet.find({ _id: id }) }
    }[name]
}

Related issues, although I'm not sure which one if any this duplicates:

#13995 the compiler doesn't narrow type parameters so even if you tested if (name === "car") it wouldn't be able to verify the return type. #27808 and #33014 are possible ways to deal with that.

#33912 the compiler really doesn't know how to verify assignability to conditional types depending on unspecified generics, and ExtractCollection<> is a conditional type.

#30581 or #25051, even if both of the above were implemented, I think you'd still need to write redundant code so that the compiler would narrow both to "car" and "pet" separately and verify that each case returns the proper type. Having a single line collections[name].find({ _id: id }) would still end up being some kind of non-generic union type. It would be nice to ask the compiler to pretend that you wrote a switch/case on name without actually having to do it.

@LinusU
Copy link
Contributor Author

LinusU commented Feb 11, 2020

Thanks for the great writeup @jcalz! ❤️

I've been going thru the linked issues to read and subscribe, lots of great info there. As you say, I'm not sure that any of those would actually fix the problem, but they should absolutely be steps in the right direction!

For now I've worked around this in my app with a simple @ts-ignore 😬

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Feb 21, 2020
@LinusU
Copy link
Contributor Author

LinusU commented Jun 22, 2021

@RyanCavanaugh would you mind commenting on why this was closed? 🤔 Do you need some updated information from me, or will this simply never be fixed?

@RyanCavanaugh
Copy link
Member

@LinusU the linked issues describe possible next steps in this area; work beyond that I think would be a large rearchitecting of many key algorithms and thus not immediately actionable (open/closed is actionable/unactionable in this repo)

@LinusU
Copy link
Contributor Author

LinusU commented Jun 23, 2021

Thanks for taking the time to respond 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

3 participants