Proper utilization of an interface with a literal object

I've been encountering some challenges with TypeScript. Let's say you have a literal object where the value is assigned using the spread operator:

const defaultState = () => {
  return {
    profile: {
      id: '',
      displayName: '',
      givenName: '',
      surName: '',
    },
  }
}

const state = reactive(defaultState())
const response = await getGraphProfile()
state.profile = { ...defaultState().profile, ...response.data }

After updating the types library @microsoft/microsoft-graph-types, the following TS error is thrown:

TS2322: Type '{ accountEnabled?: Maybe<boolean>; ageGroup?: string | null | undefined; assignedLicenses?: MicrosoftGraph.AssignedLicense[] | undefined; assignedPlans?: MicrosoftGraph.AssignedPlan[] | undefined; ... 102 more ...; surName: string; }' is not assignable to type '{ id: string; displayName: string; givenName: string; surName: string; jobTitle: string; mail: string; mobilePhone: string; officeLocation: string; businessPhones: string[]; preferredLanguage: string; userPrincipalName: string; }'.
  Types of property 'displayName' are incompatible.
    Type 'string | null' is not assignable to type 'string'.
      Type 'null' is not assignable to type 'string'.

Attempting to set the interface MicrosoftGraph.User on the literal object as shown in this answer did not resolve it, indicating a possible syntax error:

import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'

const defaultState = () => {
  return {
    profile: MicrosoftGraph.User = {
      id: '',
      displayName: '',
      givenName: '',
      surName: '',
    },
  }
}

This leads to the TS error below even though the User interface is present and correctly utilized in the function getGraphProfile.

TS2339: Property 'User' does not exist on type 'typeof import("T:/Test/Brecht/Node/prod/hip-frontend/node_modules/@microsoft/microsoft-graph-types/microsoft-graph")'.

Additional code:

import config from 'src/app-config.json'
import axios, { AxiosRequestConfig } from 'axios'
import { getToken } from 'src/services/auth/authService'
import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'

const callGraph = <T>(
  url: string,
  token: string,
  axiosConfig?: AxiosRequestConfig
) => {
  const params: AxiosRequestConfig = {
    method: 'GET',
    url: url,
    headers: { Authorization: `Bearer ${token}` },
  }
  return axios.request<T>({ ...params, ...axiosConfig })
}

const getGraphDetails = async <T>(
  uri: string,
  scopes: string[],
  axiosConfig?: AxiosRequestConfig
) => {
  try {
    const response = await getToken(scopes)
    if (response && response.accessToken) {
      return callGraph<T>(uri, response.accessToken, axiosConfig)
    } else {
      throw new Error('We could not get a token because of page redirect')
    }
  } catch (error) {
    throw new Error(`We could not get a token: ${error}`)
  }
}

export const getGraphProfile = async () => {
  try {
    return await getGraphDetails<MicrosoftGraph.User>(
      config.resources.msGraphProfile.uri,
      config.resources.msGraphProfile.scopes
    )
  } catch (error) {
    throw new Error(`Failed retrieving the graph profile: ${error}`)
  }
}

What would be the correct approach to save the property displayName as string | null?

Answer №1

The problem stems from implicit types.

const state = reactive(defaultState())

State is declared without a specific type and initialized as reactive(defaultState). This means it inherits the type of defaultState.

const defaultState = () => {
  return {
    profile: {
      id: '',
      displayName: '',
      givenName: '',
      surName: '',
    },
  }
}

defaultState has an implicit type based on the object it returns.

When we set a value to state

state.profile = { ...defaultState().profile, ...response.data }

The type of response.data is enforced as MicrosoftGraph.User where displayName: string | null.

Therefore, state.profile.displayName is expected to be of type string while response.data.displayName can also be string | null, causing a TypeScript error.

The solution

To resolve this issue, we need to provide better typing for defaultState.

const defaultState = () => {
  return {
    profile: {
      id: '',
      displayName: '',
      givenName: '',
      surName: '',
    },
  } as { profile: MicrosoftGraph.User },
}

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

Printing the result of an SQL query in Node.js without using braces

As a beginner in node.js with only a basic understanding of javascript, I apologize if I make any mistakes. I attempted to display the results retrieved by an SQL query in node.js using: <p>Users are " + util.inspect(results) + ".</p>" an ...

When the button is clicked, it triggers a change and updates the records in the database. The

There is a single button named "delete" that, when clicked by a user, will change the text to "restore" and perform a delete action in the database. The same functionality should apply to the restore button, where clicking it will change the text back to " ...

What is the reason why the show() function in JQuery only applies to one specific element, rather than all elements selected with the same selector?

JSFiddle. This example code features a nested <table> within another <table>. The main issue concerns the click listener for the #add button. Specifically, the final if/else statement in the function. When you execute this code and click the ...

Text hyperlink populates form fields

I am interested in learning how to create a clickable text link that can automatically populate specific information in a form located above it. For example, I have a donation form with fields for amount, country, and projects. Below this form, there are ...

Troubleshoot: Issue with Navbar Dropdown Expansion on Bootstrap Sass 3.3.6 with JavaScript

Beginner: Bootstrap Sass 3.3.6 - Incorporating Javascript - Issue with Navbar Dropdown Not Expanding application.html.erb Head: <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> ...

Is it possible for a React selector to retrieve a particular data type?

As a newcomer to React and Typescript, I am currently exploring whether a selector can be configured to return a custom type. Below is a basic selector that returns a user of type Map<string, any>: selectors/user.ts import { createSelector } from ...

The guidelines specified in the root `.eslintrc.json` file of an NX workspace do not carry over to the project-level `.eslintrc.json` file

In my main .eslintrc.json file, I have set up some rules. This file contains: { "root": true, "ignorePatterns": ["**/*"], "plugins": ["@nrwl/nx", "react", "@typescript-eslint", &qu ...

I need assistance with using the angular-oauth2-oidc library to retrieve data from an asynchronous storage provider and then pass it to a synchronous storage implementation

Typically, the angular-oauth2-oidc library saves tokens in session storage by default. While you can provide your own storage provider through the OAuthStorage class, it requires a storage provider that can retrieve data synchronously. I am developing a ...

What is the best way to update the color of a v-select component?

https://i.sstatic.net/6VOIo.png Currently utilizing Vuetify for my project <v-select id="makeMeBlue" dense outlined :items="form.values.urlTypes" label="Single or Multi URL" v-model="form.values.urlType" ...

Utilize the ConditionExpression to update the status only when the current status is not equal to 'FINISH'

I'm struggling to create a ConditionExpression that will only update the status in my DynamoDB table called item. Here's what I have so far: dynamo.update({ TableName, Key, UpdateExpression: 'SET #status = :status', Exp ...

Incorporate action icons (such as edit and delete) into a table in React.js using material-ui

Within my existing table, I utilized a library known as react-bootstrap-table-next This library effectively displays data in a table format with values originating from a JSON response Now, my goal is to integrate an Action column that includes options f ...

Invoke a function while rendering or utilize a getter

Is it better to use a class method or a getter when a render method needs to return a calculated value? class User extends Component { getFullName () { const { fname, lname } = this.props return `${lname}, ${fname}` } render () { return ...

Tips for invoking a function in an ASP.NET code-behind file using JavaScript

I'm currently trying to display an image in my div by calling a function from the code behind file. The image tag is dynamically bound with JavaScript, but I'm having trouble figuring out how to call a function with parameters. The code snippet I ...

Ways to stop dialog from disappearing in Reactjs

This code snippet demonstrates the implementation of a popup with buttons, where clicking the cancel button triggers a confirmation dialog. To make the popup disappear when clicked outside of it, the following event handling is employed: const popupRef = ...

Trouble accessing object property within view in Ionic 2

While working on a tutorial using Ionic 2, I've encountered an issue where I cannot access an object property in the view. Here's an example: // TypeScript file export class MyClass { myObject: any; constructor() { } ionViewDidL ...

Exploring the possibilities of implementing Undo and Redo features in freehand drawing with Reactjs on

After attempting to create a Freehand drawing using HTML5 canvas with React, my next step is to incorporate an undo and redo functionality when the corresponding buttons are clicked. I would greatly appreciate any assistance provided. function App(props) ...

Output the variables within a jade template

Is there a way to display a default value inside a textarea in my form using the code below? textarea(class="form-control", name="details") if restaurant.details #{restaurant.details} However, when the value (restaurant.details) is set, i ...

`AngularJs Controller Unit Testing with sweetalert Integration in Jasmine Framework`

When it comes to testing angularjs controllers with jasmine and karma, there seems to be an issue with testing codeblocks within the sweetalert function. How can I verify that the sweet function is being called from my test class in order to test whether $ ...

A different task executes prior to completing the jQuery ajax request, ignoring the app's designated sequence

My goal is to retrieve a response from an API in the first function and store it in an object field, then log it in the second function. The app initiates when we invoke the initApp() method, following the same sequence: fetching data first, then logging ...

Resolving DOMException issue in Google Chrome: A Step-by-Step Guide

In my browser game, I am experiencing an issue with sound playback specifically in Google Chrome. Although the sound manager functions properly in other browsers, it returns a DOMException error after playing sounds in 50% of cases. I'm unsure what co ...