1
class Component { }
class ShallowWrapper { }

// generic P is a simple object
class TestContainer<T extends Component, P extends object> 
{
    constructor(reactElement: T, selectors: P) {
        // iterate over selectors and define object properties
    }
    initialize(): void { }
    [elem: keyof P]: any 
    // I need to define class properties based on P's keys
    // -- compiler throws --
    // An index signature parameter type cannot be a union type. 
    // Consider using a mapped object type instead.
}

const p = new TestContainer(new Component, { test: 'a' });

const v = p.test // this throws a type error (property does not exist)

In the above code, I'm trying to dynamically define object properties based on generic parameter P. However the compiler throws the error

An index signature parameter type cannot be a union type. Consider using a mapped object type instead.

How do I get around this?

1 Answer 1

2

The compiler gives you this error because you are trying to mix syntax for indexed signature parameters with syntax for mapped types. Indexed signature parameters may only be of type string or number.

You could use a factory method to achieve what you want with strong typing:

class Component { }

class TestContainer<T extends Component> {
    static create<T extends Component, P extends object>(reactElement: T, selectors: P) {
        return Object.assign(new TestContainer(reactElement), selectors);
    }

    private constructor(reactElement: T) {}
}

const p = TestContainer.create(new Component(), { test: 'a' });

const v = p.test;

By the way, I don't know if you made Component empty for illustration, but you should never use an empty class for limiting types. Since TypeScript uses a structural type system an empty class is structurally equivalent to Object. This means that T extends Component is basically useless because it'd be the same as T extends Object which in turn would be the same as just T. If you try it you'll see that the following is valid:

TestContainer.create(42, { test: 'a' })
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks this works. I ended up implementing something similar. The component was there just for illustration :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.