Reputation: 485
const s: string = 'foo';
const pass1 = (origin: string) => origin.concat(s);
const pass2 = (origin: string[]) => origin.concat(s);
type S = string | string[];
const error = (origin: S) => origin.concat(s);
The code above. I can call concat
in a string
or string[]
type. So why TypeScript disallow call concat
in string | string[]
type?
The error is:
Cannot invoke an expression whose type lacks a call signature.
Type '((...strings: string[]) => string) | { (...items: ConcatArray<string>[]): string[]; (...items: (s...'
has no compatible call signatures.
Because they have different return type? But I think TS can infer error
's type is S
. Is it a intentional design? If it is, why?
Upvotes: 3
Views: 1998
Reputation: 249486
Because while the concat
method is common between the two types it has a very different signature between the two, so Typescript can't really merge the declarations of the methods. While not ideal you can use a type guard to discern between the two types:
const s: string = 'foo';
type S = string | string[];
const error = (origin: S) => typeof origin === 'string' ?
origin.concat(s) :
origin.concat(s);
Or just assert as any
:
const s: string = 'foo';
type S = string | string[];
const error = (origin: S) => (origin as any).concat(s) as S
There is also the option of transforming the union of signatures into an intersection of signatures. This may work well in some cases but not in others:
const s: string = 'foo';
type S = string | string[];
type UnionToIntersection<U> =
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
function mergeSignature<T, K extends keyof T> (value: T, method: K) : UnionToIntersection<T[K]>{
return ((...args: any[]) => (value[method] as any as Function).apply(value, args)) as any;
}
const error = (origin: S) => mergeSignature(origin, 'concat')(s);
Upvotes: 5