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?
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.
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
.
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
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>
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 😅