What sets apart obj.prop from obj['prop'] in typescript?

After utilizing @babel/parser to parse a string and receiving an AST, I am encountering an error when logging the AST by using the type of obj.prop. However, it works when using the type of obj['prop']

import { parse } from "@babel/parser";
import traverse from "@babel/traverse";

const ast = parse('{key: "something"}', {
    sourceType: "module",
    plugins: ["typescript"],
});

// Property 'declaration' does not exist on type 'Statement'.  Property 'declaration' does not exist on type 'BlockStatement'.ts(2339)
console.log(ast.program.body[0].declaration.properties);
// it's good
console.log(ast.program.body[0]["declaration"].properties);

I'm struggling to understand the difference between the two types of writing?

Thank you for any insights provided

Answer №1

One can retrieve the attributes of an object in JavaScript or typescript by:

  1. Using dot property accessor: object.property
  2. Utilizing square brackets for property access: object['property']

When to employ each method:

Opt for the dot property accessor when the property name is predetermined.

For example, if we have:

const hero = {
  name: 'XYZ'
};

We already know the property beforehand, so we can use hero.name

Choose the square bracket property accessor when dealing with dynamic property names, specifically those determined at runtime.

For instance :

const property = 'name';
const hero = {
  name: 'XYZ'
};

// Square brackets property accessor:
hero['name'];   //output => 'XYZ'
hero[property]; //output => 'XYZ'

In situations where the property is decided at runtime, square brackets should be used.

Using the dot accessor on properties that are determined at runtime will result in a typescript error:

Error: Property 'name' does not exist on type hero.

There is no right or wrong way to access properties; it all depends on the specific scenario.

For more detailed information, you can refer here.

Answer №2

In TypeScript, you have the option to access a property using either dot notation or bracket notation. The key difference between the two lies within error handling, depending on your compiler settings.

If the noImplictAny setting is set to false (which is the default), using bracket notation will not produce an error even if the property cannot be validated at compile time, resulting in an implicit any type. For example,

ast.program.body[0]["declaration"]
would be inferred as any, with TypeScript failing to check any subsequent .properties. You can test this behavior in the playground.

Conversely, setting noImplictAny to true will prompt TypeScript to flag potential issues where it cannot verify that declaration indeed exists within ast.program.body[0]. This will result in errors for both instances, though they may vary slightly. You can experiment with this behavior in the playground.

To ensure safe access of ast.program.body[0] properties, consider narrowing its type prior to retrieval. Below is an approach demonstrating how to narrow ast.program.body[0] from a generic statement to an object expression with a defined properties member:

import { parse } from "@babel/parser";
import { isExpressionStatement, isObjectExpression } from "@babel/types";

const ast = parse('({key: "something"})', {
    sourceType: "module",
    plugins: ["typescript"],
});

let firstStatement = ast.program.body[0];
if (!isExpressionStatement(firstStatement) || !isObjectExpression(firstStatement.expression)) {
    throw Error("Expected statement not found");
}

console.log(firstStatement.expression.properties);

Explore further in the TypeScript playground.

Please note that I modified {key: "something"} to ({key: "something"}) assuming you intended to parse an object literal. Your original version resembled a block statement with a labeled statement inside, distinguishable here: Block statement vs Object literal.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

tsc is not able to identify virtual fields in a mongoose schema

I'm having trouble getting mongoose virtual to work in typescript. Following the instructions outlined in "another approach" on mongoose documentation, I used mongoose's InferSchemaType to create the interface. However, TSC does not recognize t ...

What strategies can you employ in Angular to reverse the outcome of a guard using canActivate?

As per the Angular documentation regarding canActivate, it appears that you can only utilize canActivate guards to allow navigation to a route if the canActivate function ultimately returns true. Is there any way to specify, "proceed to this route only if ...

In TypeScript, this regular expression dialect does not permit the use of category shorthand

Recently, I attempted to implement a regular expression in TypeScript: I ran the following code: const pass = /^[\pL\pM\pN_-]+$/u.test(control.value) || !control.value; To my surprise, an error occurred: "Category shorthand not allowed in ...

Importing from the project root is a common practice in Typescript

My project structure is organized as follows: .dist classes namespace1 module.js public routes index.js app.js config.js src classes namespace1 module.ts public routes index.ts app.ts config.ts The .dist f ...

I am having trouble getting the guide for setting up a NextJS app with Typescript to function properly

For some time now, I have been experimenting with integrating Typescript into my NextJS projects. Initially, I believed that getting started with Typescript would be the most challenging part, but it turns out that installing it is proving to be even more ...

Is there a way to access the value of the --base-href parameter in my Angular project during the build process?

In my Angular project, I have set up multiple environments for different stages of development, testing, acceptance, and production. Each environment has a specific base URL, which I designate using the --base-href flag during the project build. However, I ...

In React, the Textarea component that displays the character count only updates its value after a page reload

Contained within this element is the following component: import React, { useEffect, useState } from 'react'; import { TextareaTestIds } from '../utils/test-ids'; export interface TexareaProps extends React.TextareaHTMLAttributes<H ...

What is the specific reference of the first parameter in NLS localize?

Regarding the question on localizing VSCode extensions, I am also curious why the localize function requires two parameters. When it comes to localizing settings, it's a simple process of replacing literal values with tokens surrounded by percent sig ...

Is there a way to set the submitted variable to true when the form group is submitted, then revert it to false when the user makes changes to the form?

With just one FormGroup, I ensure that when a user submits the form with errors the 'submitted' variable is set to true, displaying the errors. However, my challenge now is how to reset this variable to false when the user makes any changes after ...

The error TS2769 occurs when using the spread operator to flatten an array

I've been working on flattening an array, but unfortunately I keep encountering an error - TS2769: No overload matches this call. Oddly enough, when I tested this in stackblitz, it worked perfectly. The issue seems to be related to the spread operat ...

TypeScript incorporates a variety of @types versions for react

I made changes to my compilerOptions within the tsconfig.json file with the specified paths "paths": { "react": ["node_modules/@types/react"], "@types/react": ["node_modules/@types/react"] } However, I noticed that @types/react-router is using its o ...

Issue: Error thrown due to attempting to access the 'push' property of an undefined element in an Angular child component

I have an array in my child component's element @Input() listAnswer: any; changestyle(event) { let activeSpan = event.target; this.listAnswer.push(activeSpan.innerText.trim()); } I am passing this variable from the parent component < ...

Tips for conducting key down event testing on a material ui MenuList element utilizing react-testing-library

Looking to test the key down event on my MenuList component. Component: import MenuItem from '@material-ui/core/MenuItem'; import MenuList from '@material-ui/core/MenuList'; import * as React from 'react'; export default fu ...

Using an external HTML file to import a template into Vue.js single file components

I've been tackling a Vuejs project that involves using vue-property-decorator in single file components. I'm trying to figure out how to import the template from an external HTML (or different) file, but so far I haven't found a solution. I& ...

Attempting to change the id property into a Mongoose ObjectId

Here is a snippet of my code: acceptContactRequest: async ( _: any, { userId }: IUserInfo, context: ExpressContext ) => {.....} The interface IUserInfo looks like this: export interface IUserInfo { username: string; password: ...

Is there a way to retrieve the value of a particular attribute while hovering the mouse over it?

When I hover my mouse over the innerHTML content, certain words are highlighted with a title attribute value. How can I retrieve the specific title value of the content I am hovering over? This should be done using the mouseover event in the TypeScript fil ...

Guide on accomplishing masking in Angular 5

I'm curious if it's achievable to design a mask in Angular 5 that appears as follows: XXX-XX-1234 Moreover, when the user interacts with the text box by clicking on it, the format should transform into: 1234121234 Appreciate your help! ...

A guide on refreshing the dependencies list within Angular's node modules and package.json files

A close friend sent me the angular src folder, which I used to create a new Angular project. However, when I replaced my newly created src folder with my friend's and tried running the application using npm start, I encountered errors related to missi ...

Category of ambiguous attributes

I am currently working on developing a class that will store values for rows and columns, with each row/column identified by a string. I have provided some code below as an example: class TMatrix<TYPE>{ [idRow: string]: { [idCol: string]: TYPE ...

Encountering data loss while mapping for the first time in React Native using useState - any solutions?

const initialData = [ { id: 1, name: `${accountList?.brideName} ${accountList?.brideSurname}`, type: 'Gelin', selected: false, tlText: accountList?.brideAccount?.accountTl, euroText: accountList?.brideAccou ...