microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
99.1k stars 12.29k forks source link

Cannot use symbol as "index" for plain object property access #50319

Open mhofman opened 1 year ago

mhofman commented 1 year ago

Bug Report

{ [prop]() { } }[prop] fails with Type 'symbol' cannot be used as an index type. aka ts(2538) if prop: string | symbol. It works fine if prop is either string or symbol.

🔎 Search Terms

union type cannot be used as an index type

Found 2 somewhat relevant issues and a pull request, but they don't seem to directly apply. Please close if duplicate:

🕗 Version & Regression Information

⏯ Playground Link

Playground link with relevant code

💻 Code

declare function assert(condition: any, msg?: string): asserts condition;

function wrap(x: any) {
    return new Proxy(x, {
        get(_target, p, receiver) {
            return {
                [p](...args: any[]) {
                    assert(this === receiver);
                    return Reflect.apply(x[p], x, args);
                },
            }[p]
        },
        has() {
            return true;
        }
    });
}

declare const prop: string | symbol;

const shortHandFn = { [prop]() { } }[prop];

🙁 Actual behavior

Type 'symbol' cannot be used as an index type.

🙂 Expected behavior

No type error since both symbol and string are allowed index types.

icecream17 commented 1 year ago

ts(2538) ts2538 Also ran into this

https://www.typescriptlang.org/play?target=99#code/GYVwdgxgLglg9mABAWwIYGsCmAxc15gDqMUAFgHKrKaIAUYVmAXIgAoBOcADpu1AJ4BpTPwCUiAN4AoRLMTtMUEOyTS56xAG0G1ALota4gLwA+ROAAmmYDDCYLMuQF9tjXVKdSgA

function makeFunctionWithName (name: PropertyKey) {
    return {
        [name]: () => undefined
    }[name]
}
n-rowe commented 1 month ago

I'm still getting this error, is there any workarounds? I'm using Vue and the inject/provide syntax uses symbols for accessing stored values.

https://www.typescriptlang.org/play/?target=99#code/JYOwLgpgTgZghgYwgAgJIgFYQWYB7EAaQgE8AeAFQD5kIAPSEAEwGdkBlEgWwCM8AbZAG8AvgCgEBFmGQAHKHgBuwJhDYBeYcnFiYAVxA58IOQuWrKVABQBrUgC40mbLgLFy1ADTJFcfnohHCgBKYTFkCNMlFTUAbTsSAF1kTV9-CDEdfUNXE1AsHEtbBycC3PdLYKCwyOQoCDA9KBN5aNUWeNJEzKA

interface InjectionKey<T> extends Symbol {}
const provides = { }

function provide<T>(key: InjectionKey<T>, value: T) {
    provides[key] = value
}

function inject<T>(key: InjectionKey<T>): T {
    return provides[key]
}
rain84 commented 1 month ago

@mhofman

I hope, this solution will help

const shortHandFn = ({ [prop]() {} } as T)[prop];

// biome-ignore lint:
type Fn = (...args: any[]) => any;
type T = {
  [k in string | symbol]: Fn;
};