2

I have an Angular 2 project with TypeScript. I don't understand why TypeScript live compiler is complaining about the info.map?

Error message: unresolved function or method map()

When I run it in the browser, is working just fine, but I don't understand why it happens and more importantly how TypeScript determines in which if /else branch I'm in.

The code:

let addInfo = (info: string|string[]) =>  {
        if(_.isString(info)){

            console.log('info is a string');
            infos.push(info);

        }else if(_.isArray(info)){

            console.log('info is an array');
            info.map( x => { infos.push(x) }); // here is the compiling error- map is red.
        }
 }

Here is a snapshot:

enter image description here

LazyOne
  • 158,824
  • 45
  • 388
  • 391
AIon
  • 12,521
  • 10
  • 47
  • 73
  • 1
    Typescript compiler doesn't recognise lodash's function call as a type check. You can try "if(info instanceof String)" and in the else branch typescript can assume info is array. – TSV Sep 30 '16 at 14:26
  • 1
    Do you use https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/lodash/lodash.d.ts ? – Tamas Hegedus Sep 30 '16 at 14:31

2 Answers2

3

Typescript compiler doesn't recognise lodash's function call as a type check. You can try "if(info instanceof String)" and in the "else" branch typescript can assume the "info" is array.

This works for me in the typescript playground:

let addInfo = (info: string|string[]) =>  {
    if(info instanceof String){
        info;
    } else {
        info.map(s => s);
    }
 }

Update

Thanks to @Tamas Hegedus, shared knoweledge about custom type-guards. I haven't heard about them before. I've decided to put here quotation from Typescript documentation about User-Defined Type Guards:

It just so happens that TypeScript has something called a type guard. A type guard is some expression that performs a runtime check that guarantees the type in some scope. To define a type guard, we simply need to define a function whose return type is a type predicate:

function isFish(pet: Fish | Bird): pet is Fish {
    return (<Fish>pet).swim !== undefined;
}

pet is Fish is our type predicate in this example. A predicate takes the form parameterName is Type, where parameterName must be the name of a parameter from the current function signature.

TSV
  • 7,538
  • 1
  • 29
  • 37
  • Yes, that is a good explanation. Thanks @TSV :) But do you know if it's possible to make Typescript recognize the lodash functions? - they are so nice compared to the instanceof / or other native JavaScript checks.. Webstorm is underlining in red all project files that have this types errors and i have multiple files in red because of this. I don't know which are the real errors and which are not. Is annoying.. – AIon Sep 30 '16 at 14:37
  • I do understand your point. I'm not sure typescript understands another type checknig except it's own... Are you using "lodash.d.ts"? If so, probably I'm right... – TSV Sep 30 '16 at 14:41
  • @Alon Typescript has user-defined type guards, and lodash.d.ts takes advantage of them. Just use it and the compiler will not complain anymore. – Tamas Hegedus Sep 30 '16 at 15:00
  • 2
    You should really use `typeof info === 'string'` -- `instanceof` is going to give you false positives on wrapped `String` objects which won't behave the way you expect. It's also likely to be faster to use `typeof` – Ryan Cavanaugh Sep 30 '16 at 21:13
3

Simple solution:

I had the @types/lodash/index.d.ts file, but i believe was an older version. As soon as I installed the new version - everything worked as expected.

So for people with Angular2 CLI project, just run: npm install @types/lodash :)

AIon
  • 12,521
  • 10
  • 47
  • 73