Skip to content

Generics and keyof does not work with computed properties #14473

Closed
@altschuler

Description

@altschuler

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'.

Activity

kemsky

kemsky commented on Mar 10, 2017

@kemsky

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

altschuler commented on Mar 13, 2017

@altschuler
Author

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

kevinjhanna

kevinjhanna commented on Mar 16, 2017

@kevinjhanna

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

MadaraUchiha commented on Jun 11, 2017

@MadaraUchiha

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)

added this to the TypeScript 2.5 milestone on Jun 14, 2017
sandersn

sandersn commented on Jul 25, 2017

@sandersn
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

sandersn commented on Jul 25, 2017

@sandersn
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.

added
FixedA PR has been merged for this issue
on Aug 3, 2017
jcalz

jcalz commented on Sep 21, 2017

@jcalz
Contributor

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

sandersn

sandersn commented on Sep 21, 2017

@sandersn
Member

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

jcalz

jcalz commented on Jun 15, 2018

@jcalz
Contributor

^ @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

danilofes commented on Jun 19, 2018

@danilofes

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

davidfarinha commented on Sep 10, 2018

@davidfarinha

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

iDaN5x commented on Mar 22, 2019

@iDaN5x

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

towry commented on Jun 25, 2019

@towry

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

8ec7b872-5852-4c83-bef6-d716556685f7

davidfarinha

davidfarinha commented on Jun 25, 2019

@davidfarinha

@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

Jacob-Gray commented on Aug 1, 2019

@Jacob-Gray

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

towry commented on Aug 2, 2019

@towry
sliminality

sliminality commented on Sep 25, 2019

@sliminality

Still a problem in 3.6 :(

vlanemcev

vlanemcev commented on Apr 24, 2020

@vlanemcev

REOOOOPEEEEN TAAASK!!!!!

PengBoUESTC

PengBoUESTC commented on Oct 27, 2021

@PengBoUESTC

how to fix this problem! plz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFixedA PR has been merged for this issue

Type

No type

Projects

No projects

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @sliminality@jcalz@sandersn@kevinjhanna@altschuler

      Issue actions

        Generics and keyof does not work with computed properties · Issue #14473 · microsoft/TypeScript