0

In VSCode, IntelliSense is saying a property does not exist on an object in a TypeScript file even when I specify the optional chaining syntax to short-circuit the error. The code compiles correctly to JavaScript es2020.

Here is the typescript code

"use strict"
const animals = {
    dog: {
        name: 'Rover'
    }
};
const catName = animals.cat?.name;          ** *<<<<< the squiggly line appears under cat***
console.log(catName);

The actual TypeScript error is

    error TS2339: Property 'cat' does not exist on type '{ dog: { name: string; }; }'.

I understand that the VSCode ships with its own version of Typescript so I added the following to my workspace settings.

{
    "typescript.tsdk": "node_modules/typescript/lib",
}

When I hover over the typescript version number on the VSCode status bar it shows the following which is a valid path.

D:\projects\test-ts\node_modules\typescript\lib\tsserver.js

I also tried disabling all VSCode Extensions however this did not change the result.

My VSCode version is 1.88.1 My TypeScript version is 5.45

FYI, I'm using Windows 10.

All my research and review of other posts lead back to the typescript’s "typescript.tsdk" setting but this has not solved the problem.

Below is my tsconfig.json file.

{
"compilerOptions": {
    "target": "es2020",
    "module": "commonjs",
    "rootDir": "./src",
    "outDir": "./js",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
},
  "include": [
    "src"
  ]
}

How can I eliminate the error that is being displayed in VSCode?

1 Answer 1

4

There is nothing wrong with your TypeScript installation, in fact it's doing exactly what it should be doing. Let's break down the issue here:

const animals = {
    dog: {
        name: 'Rover'
    }
};

You have a constant definition that has an inferred type, which could explicitly be written like so

type Animals = {
  dog: {
    name: string
  }
}

If you want this to be more flexible, then you will need to either extend the type manually or loosen the type altogether.

Solution #1

Let's say you want all sorts of animals and it doesn't matter what they are called (i.e. cat, dog, bird...) you could do something like this:

type Animals = {
  [key: string]: {
    name: string
  }
}

const animals: Animals = {
    dog: {
        name: 'Rover'
    }
};

const catName = animals.cat?.name; 
console.log(catName);

Here we have a type Animals which can contain any type of animal, which is just an object with the property name.

Solution #2

If you want to explicitly only allow animals which are either cat or dog then you could do the following

type Pet = {
  name: string
}

type Animals = {
  cat?: Pet,
  dog?: Pet,
}

const animals: Animals = {
    dog: {
        name: 'Rover'
    }
};

const catName = animals.cat?.name; 
console.log(catName);

NOTE: in this case it's possible for both cat and dog to both be defined at the same time, both undefined at the same time, or a combination of defined and undefined

Solution #3

If you want these objects to behave like normal JS objects which can contain basically any key / value, you could also use a looser type like Record

const animals: Record<string, any> = {
    dog: {
        name: 'Rover'
    }
};

const catName = animals.cat?.name; 
console.log(catName);

I would recommend against using any when possible as it somewhat defeats the purpose of TS, but you could also replace that any with whatever type you wanted, for example

const animals: Record<string, { name: string }> = {
    dog: {
        name: 'Rover'
    }
};

or even better

const animals: Partial<Record<'cat' | 'dog', { name: string }>> = {
    dog: {
        name: 'Rover'
    }
};

How to use TypeScript Record<key, value>

Additional Comments

TypeScript is showing an error here because from it's perspective you are trying to access .cat on an object which does not contain any such property.

This is actually why TS is incredibly useful, especially in large teams, because it helps catch semantic issues prior to run-time.

This is also why TS can be annoying, especially when refactoring old JS codebases which can be quite large. Sometimes you just wanna get the code working and not fiddle with the types, in which case you can do the following

const animals: any = {
    dog: {
        name: 'Rover'
    }
};

By using the type any you are basically specifying the type can literally be anything, but this somewhat defeats the purpose of TS in the first place 😅

TypeScript Do's & Don'ts of Any

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for your reply. Since my original code does not contain “any” TypeScript specific code, I can just rename it to have a .js extension and it runs without any errors. This is true because of the optional chaining character ? after animals.cat short circuits error and returns undefined without throwing an error. Exactly as intended. So, what I’m trying to understand is why the error is showing in VSCode when using TypeScript when there is no syntax error and the code does not throw a runtime error. It seems to me that VSCode is incorrectly displaying an error w/using typescript.
@RobW yeah no worries, basically in this regard you can think of TS like a really advanced spell checker and nothing more. The linter isn't saying the code is wrong, it's warning you that you probably did something you didn't intend to do (i.e. checking for the property cat on an object which does not contain and will never contain cat). I'll update my answer with a little more detail

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.