Skip to content

Generics and keyof does not work with computed properties #14473

New issue

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

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

Already on GitHub? # 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

# for free to join this conversation on GitHub. Already have an account? # 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