TypeScript: Generics and keyof does not work with computed properties

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

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 17
  • Comments: 19 (5 by maintainers)

Most upvoted comments

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

REOOOOPEEEEN TAAASK!!!

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.

^ @sandersn

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

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)?

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