Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.5k views
in Technique[技术] by (71.8m points)

reactjs - Typescript - ... has no compatible call signatures

i get the no compatible call signatures error and dont know what to do to make it work.

React-State

     this.state = {
    categoryState: ...,
    categoryItemId
    }

Category declaration

let category: Cat1[] | Cat2[] | Cat3[] = this.getCategory();

getMethod to return category

private getCategory() {
        switch (this.state.categoryState) {
            case "Cat1": {
                return this.props.cat1 as Cat1[];
            }
            case "EVENT": {
                return this.props.cat2 as Cat2[];
            }
            default: {
                return this.props.cat3 as Cat3[];
            }
        }
    }

return-Method (react) this is where the error happens:

<select value={this.state.categoryItemID} onChange={this.handleSomething}>
                                {(category && category.length > 0) &&
                                category.map((item: any) => {
                                    return (
                                        <option value={item.id}
                                                key={item.id}>{item.localizations[this.props.currentLanguage].title}</option>
                                    );
                                })
                                }
                            </select>

error message: TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((callbackfn: (value: Cat1, index: number, array: Cat1[]) => U, thisArg?: any) => U[] |...' has no compatible call signatures.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Update 2019-03: With the release of TypeScript 3.3, there has there has been progress for calling a union of function types, but unfortunately this pain point still exists for generic methods like Array.prototype.map():


This is a pain point in TypeScript: there's no way to call a "union of function types" unless either they accept exactly the same arguments, or starting in TS3.3, if at most one element of the union of functions is generic or overloaded. In your case, since category is Cat1[] | Cat2[] | Cat3[], the method category.map() is itself a union of generic functions whose parameters are not the same; and you can't call that. It should be safe to call category.map(f) if f is a function which accepts Cat1 | Cat2 | Cat3 (this may seem obvious, but involves understanding contravariance of method arguments, which is easy to get mixed up). But, as the linked issue mentions, the compiler doesn't let you do that:

This is currently by design because we don't synthesize an intersectional call signature when getting the members of a union type -- only call signatures which are identical appear on the unioned type.

So you get an error. Luckily there are workarounds. The easiest in your case is to treat category not as Cat1[] | Cat2[] | Cat3[], but as (Cat1 | Cat2 | Cat3)[]. As long as you're just reading from the array and not writing to it, it should be safe to do that:

let category: Cat1[] | Cat2[] | Cat3[] = this.getCategory();
const mappableCategory = category as (Cat1 | Cat2 | Cat3)[];

Now you should be able to map over mappableCategory instead of category:

mappableCategory.map((item: any) => { /* ... */ }); // should work now.

(Just to reiterate: reading from mappableCategory is fine, but writing to mappableCategory could be a mistake, since it will happily accept elements of Cat1, even if the underlying category was an array of Cat2. So don't write to it unless you're sure what you're doing.)

Hope that helps you. Good luck!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...