Skip to content

Generics and keyof does not work with computed properties #14473

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
altschuler opened this issue Mar 5, 2017 · 19 comments
Closed

Generics and keyof does not work with computed properties #14473

altschuler opened this issue Mar 5, 2017 · 19 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@altschuler
Copy link

altschuler commented Mar 5, 2017

TypeScript Version: 2.2.0

Code with unexpected type error:

export function set<T, K extends keyof T>(obj: T, key: K, value: T[K]) {
    return Object.assign(obj, {
        [key]: value,
    });
}

Expected behavior:
No type errors because K should is a subtype of keyof T. It works if key: keyof T and even key: K & keyof T.

Actual behavior:
Type error:
A computed property name must be of type 'string', 'number', 'symbol', or 'any'.

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Mar 7, 2017
@kemsky
Copy link

kemsky commented Mar 10, 2017

I was almost desperate trying to get similar code working:

class Options<T extends object, K extends keyof T>
{
    [key:K]:T[K];

    constructor(value:T)
    {
        Object.assign(this, value);
    }
}

Also I wonder, is it possible to avoid declaring second type parameter (K)?

@altschuler
Copy link
Author

@kemsky You could simply replace all occurrences of K with keyof T.

@kevinjhanna
Copy link

Here there is another example that fails with the same error

const changeValue = <K extends keyof State> (key: K, value: string) => {
  return (prevState: State, props: Props): Pick<State, K> => {
    return {
      [key]: value
    }
  }
}

In addition to the error @altschuler gets, I get:
Type '{ [x: string]: string; }' is not assignable to type 'Pick<State, K>'

State:

interface State {
 foo: string
}

@MadaraUchiha
Copy link

MadaraUchiha commented Jun 11, 2017

Bump, this still happens with 2.3.4, are there any plans to fix/implement this? cc @RyanCavanaugh (You're the only collaborator to interact with this issue)

@mhegazy mhegazy added this to the TypeScript 2.5 milestone Jun 14, 2017
@sandersn
Copy link
Member

The error message should be checking the apparent type of key. The apparent type would correctly resolve to the type parameter's K's constraint, which is string-like, instead of the type parameter K itself, which is not itself string-like.

@sandersn
Copy link
Member

Fixed in #17404. The apparent type turned out not be quite right because that also converts string to String and so on. Instead I just get the type parameter's constraint and use that if there is one.

@jcalz
Copy link
Contributor

jcalz commented Sep 21, 2017

The error from @kevinjhanna's comment is still occurring in TS 2.5 and above. Is this intended?

@sandersn
Copy link
Member

Yes, this needs @rbuckton's PR for binding of dynamic names to work: #15473

@jcalz
Copy link
Contributor

jcalz commented Jun 15, 2018

^ @sandersn

So, that error from @kevinjhanna's comment is still occurring in TS 2.9. Is this intended? Does it need a new issue?

@danilofes
Copy link

Hi,
I also have a problem that may be realated to this issue (I'm using TS 2.9.2):
https://stackoverflow.com/a/50929895/2292481

@davidfarinha
Copy link

davidfarinha commented Sep 10, 2018

Hello,
This has been implemented now but isn't in the type-system if you wanted to strongly type usages of computed properties. For example, here I'm trying to use mapped types with computed properties (I've simplified the following code/example so the purpose of the function might not make much sense practically but you can see the idea).

merge (target, source) {
    Object.keys(source).forEach(key => {
      Object.assign(target, { [key]: {} });
    });
}

And attempting to strongly typing this in an ambient module (for a JS framework) and it's currently not possible. For example:

declare function  merge<Target extends {}, Source extends {}>(target: Target, source: Source): Target extends object ?
    Source extends object ? {
      [Key in keyof Source]: Target & { [Key]: object } // Error here at [Key] - "'Key' only refers to a type, but is being used as a value here."
    } : never
  : Target;

@iDaN5x
Copy link

iDaN5x commented Mar 22, 2019

This is still occurring in TS v3.3:

export async function* pick<TItem, TKey extends keyof TItem>(
    iterable: Iterable<TItem> | AsyncIterable<TItem>,
    ...keys: TKey[]
): AsyncIterable<Pick<TItem, TKey>> {
    for await (const item of iterable) {
        yield {
            [key in keys]: item[key]
        };
    }
}

Error: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.

@towry
Copy link

towry commented Jun 25, 2019

How to fix this TS kind error ?????

8ec7b872-5852-4c83-bef6-d716556685f7

@davidfarinha
Copy link

davidfarinha commented Jun 25, 2019

@towry You should be able to fix that by explicitly defining the type of the indexer as a string (number, symbol or 'any') as per the error message:

let variable = {
   [a: string]: 123
};

@Jacob-Gray
Copy link

As far as I can tell, typescript currently can't process keyof a generic when used within a computed property, and instead always converts it to string:

class Wrapper<T> {
    Inner<K extends keyof T>(key: K, value: T[K]) {
        // All good
        const x: Partial<T> = {};
        x[key] = value;

        // Type '{ [x: string]: T[K]; }' is not assignable to type 'Partial<T>'.
        const y: Partial<T> = {
            [key]: value,
        };
    }
}

https://www.typescriptlang.org/play/index.html#code/MYGwhgzhAEDqBOYAOSCm8A8AVAfNA3gFDQnQCSAdhehgNLSoAeALqhQCYwDWqAngPYAzaLgAUPXgC5otADTQAbmBABXVNKwBtWgF0AlAWKljAehNx+8LhCPGSwfhQjNojaQAUw8ZgEtl2PABeAgBfAG5bO0ZNCR1oYKVVVAjI03MsXjRoAHJ8aE03aGd4HwoAcx0NbR0w6BDs6B8YCn4XSAgfMoowACMQVGhmfkHMgezPbz8QAOyAOlTSBycXKWgJ339ceMM7XfzY6US1WQWScMiQwhCgA

You can of course just as any them to force it to work, but that's a pretty garbage solution.

@towry
Copy link

towry commented Aug 2, 2019

@davidfarinha

屏幕快照 2019-08-02 下午5 14 31

@sliminality
Copy link

Still a problem in 3.6 :(

@vlanemcev
Copy link

REOOOOPEEEEN TAAASK!!!!!

@PengBoUESTC
Copy link

how to fix this problem! plz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests