Skip to content

string is not assignable to unbounded generic type T #18397

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

Closed
cliffkoh opened this issue Sep 11, 2017 · 5 comments
Closed

string is not assignable to unbounded generic type T #18397

cliffkoh opened this issue Sep 11, 2017 · 5 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@cliffkoh
Copy link

TypeScript Version: 2.4.2 / Typescript playground

Code

export interface Storage {
    getItem<T>(onComplete?: OnComplete<T>): Promise<T>;
}

export type OnComplete<T> = (err?: any, result?: T) => any;

let storage: Storage = {
    getItem: (onComplete: OnComplete<string>) => {
        return Promise.resolve("Yay");
    }
};

Typescript playground link:
https://www.typescriptlang.org/play/#src=export%20interface%20Storage%20%7B%0D%0A%20%20%20%20getItem%3CT%3E(onComplete%3F%3A%20OnComplete%3CT%3E)%3A%20Promise%3CT%3E%3B%0D%0A%7D%0D%0A%0D%0Aexport%20type%20OnComplete%3CT%3E%20%3D%20(err%3F%3A%20any%2C%20result%3F%3A%20T)%20%3D%3E%20any%3B%0D%0A%0D%0Alet%20storage%3A%20Storage%20%3D%20%7B%0D%0A%20%20%20%20getItem%3A%20(onComplete%3A%20OnComplete%3Cstring%3E)%20%3D%3E%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20Promise.resolve(%22Yay%22)%3B%0D%0A%20%20%20%20%7D%0D%0A%7D%3B%0D%0A

Expected behavior:
Code snippet above works

Actual behavior:
Ultimately, string is not assignable to type T on Typescript playground. On 2.4.2, I get string is not assignable to T | undefined. Both makes no sense...

@aluanhaddad
Copy link
Contributor

That is the correct behavior. The interface declares a generic method getItem<T> method which returns a different type of value depending on what you pass to it. The object you are attempting to implement that interface with does not meet that requirement.

@ahejlsberg
Copy link
Member

This is working as intended and is an effect of #16368. The type checker is pointing out that it is not safe to treat the type { getItem: (onComplete: OnComplete<string>) => Promise<string>; } as the more general type { getItem<T>(onComplete?: OnComplete<T>): Promise<T>; }. Basically, you can't treat a method that operates on the specific type string as a method that operates on any type T.

I suspect you probably want to write the code this way:

export interface Storage<T> {
    getItem(onComplete?: OnComplete<T>): Promise<T>;
}

export type OnComplete<T> = (err?: any, result?: T) => any;

let storage: Storage<string> = {
    getItem: (onComplete: OnComplete<string>) => {
        return Promise.resolve("Yay");
    }
};

@ahejlsberg ahejlsberg added the Working as Intended The behavior described is the intended behavior; this is not a bug label Sep 11, 2017
@cliffkoh
Copy link
Author

Thank you for the explanation. I hit this problem with the definition of Storage as defined here: https://github.com/rt2zz/redux-persist/blob/master/type-definitions/index.d.ts

I will submit a PR for them to fix per your suggestion @ahejlsberg.

Also, for the longest time, I've been confused about what it means when <T> is on a method but not on the top-level interface. Seeing the amount of similar mistakes including for typings on React-Redux in the past, perhaps the team could consider augmenting the Typescript documentation on generics to explain this particular case as well?

https://www.typescriptlang.org/docs/handbook/generics.html

Thank you again for the explanation.

@ahejlsberg
Copy link
Member

@cliffkoh Just FYI, the Storage type in that definition file is a mess. It has type parameters that are never used at all (which are pointless) and type parameters that never occur in a parameter position (which means you must always explicitly specify them).

@cliffkoh
Copy link
Author

@ahejlsberg Thanks for the FYI. Because of your FYI I took a deeper look and have updated the PR to hopefully addressed the concerns you expressed above (link to PR above), in the interests of the entire Typescript community having better typings to work with.

Mostly removed the use of generics bar the generics expressed in Mozilla's localForage library for their Storage API (under LocalForageDbMethods): https://github.com/localForage/localForage/blob/master/typings/localforage.d.ts

@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants