aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.41k stars 2.11k forks source link

Datastore update with typescript gets wrong type #9061

Closed ShreyasBhandari closed 1 year ago

ShreyasBhandari commented 2 years ago

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

Authentication, GraphQL API, DataStore, Storage

Amplify Categories

No response

Environment information

``` # Put output below this line System: OS: macOS 11.5.1 CPU: (4) x64 Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz Memory: 63.45 MB / 8.00 GB Shell: 5.8 - /bin/zsh Binaries: Node: 12.16.3 - /usr/local/bin/node npm: 6.14.4 - /usr/local/bin/npm Browsers: Chrome: 94.0.4606.81 Safari: 14.1.2 npmPackages: @aws-amplify/api: ^3.3.3 => 3.3.3 @aws-amplify/auth: ^3.4.34 => 3.4.34 @aws-amplify/core: ^3.8.24 => 3.8.24 @aws-amplify/datastore: ^2.10.1 => 2.10.1 @aws-amplify/storage: ^3.4.4 => 3.4.4 @craco/craco: ^6.2.0 => 6.2.0 @date-io/date-fns: ^1.3.13 => 1.3.13 @emotion/css: ^11.1.3 => 11.1.3 (10.0.27) @hookform/devtools: ^2.2.1 => 2.2.1 @hookform/error-message: 0.0.5 => 0.0.5 @material-ui/core: ^4.11.4 => 4.12.3 @material-ui/icons: ^4.9.1 => 4.11.2 @material-ui/lab: ^4.0.0-alpha.58 => 4.0.0-alpha.60 @material-ui/pickers: ^3.3.10 => 3.3.10 @react-pdf/renderer: ^1.6.13 => 1.6.16 @reduxjs/toolkit: ^1.5.1 => 1.6.1 @reduxjs/toolkit-query: 1.0.0 @reduxjs/toolkit-query-react: 1.0.0 @sentry/react: ^6.13.3 => 6.13.3 @testing-library/jest-dom: ^5.12.0 => 5.14.1 @testing-library/react: ^11.2.7 => 11.2.7 @testing-library/user-event: ^12.6.0 => 12.8.3 @types/file-saver: ^2.0.3 => 2.0.3 @types/jest: ^26.0.24 => 26.0.24 @types/lodash: ^4.14.172 => 4.14.172 @types/node: ^16.4.7 => 16.9.0 @types/react: ^17.0.15 => 17.0.20 @types/react-dom: ^17.0.9 => 17.0.9 @types/react-virtualized: ^9.21.12 => 9.21.13 @typescript-eslint/eslint-plugin: ^4.24.0 => 4.31.0 @typescript-eslint/parser: ^4.24.0 => 4.31.0 aws-amplify: ^3.4.3 => 3.4.3 babel-eslint: ^10.1.0 => 10.1.0 clsx: ^1.1.1 => 1.1.1 craco-alias: ^3.0.1 => 3.0.1 date-fns: ^2.23.0 => 2.23.0 dayjs: ^1.10.3 => 1.10.6 eslint: ^7.26.0 => 7.32.0 eslint-config-react-app: ^6.0.0 => 6.0.0 eslint-plugin-flowtype: ^5.7.2 => 5.9.2 eslint-plugin-import: ^2.23.2 => 2.24.2 eslint-plugin-jsx-a11y: ^6.4.1 => 6.4.1 eslint-plugin-react: ^7.22.0 => 7.25.1 eslint-plugin-react-hooks: ^4.2.0 => 4.2.0 eslint-plugin-testing-library: ^4.10.1 => 4.12.2 (3.10.2) file-saver: ^2.0.5 => 2.0.5 fontsource-roboto: ^3.0.3 => 3.1.5 history: ^5.0.0 => 5.0.1 husky: ^4.3.8 => 4.3.8 i18next: ^19.8.4 => 19.9.2 i18next-browser-languagedetector: ^6.1.1 => 6.1.2 i18next-http-backend: ^1.2.4 => 1.3.1 lint-staged: ^10.5.4 => 10.5.4 lodash: ^4.17.21 => 4.17.21 logrocket: ^1.3.0 => 1.3.0 material-ui-dropzone: ^3.5.0 => 3.5.0 prettier: ^2.3.0 => 2.4.0 react: ^17.0.1 => 17.0.2 react-contextmenu: ^2.14.0 => 2.14.0 react-csv: ^2.0.3 => 2.0.3 react-data-grid: 7.0.0-beta.0 => 7.0.0-beta.0 react-data-grid-addons: ^7.0.0-alpha.24 => 7.0.0-alpha.24 react-date-picker: ^8.2.0 => 8.3.2 react-dom: ^17.0.1 => 17.0.2 react-error-boundary: ^3.1.3 => 3.1.3 react-hook-form: ^6.15.7 => 6.15.8 react-i18next: ^11.8.15 => 11.12.0 react-infinite-scroll-component: ^5.1.0 => 5.1.0 react-redux: ^7.2.4 => 7.2.5 react-router: ^6.0.0-beta.0 => 6.0.0-beta.3 react-router-dom: ^6.0.0-beta.0 => 6.0.0-beta.3 react-scripts: 4.0.1 => 4.0.1 react-toastify: ^7.0.4 => 7.0.4 react-virtualized: ^9.22.2 => 9.22.3 redux: ^4.1.0 => 4.1.1 (3.7.2) typescript: ^4.3.5 => 4.4.2 web-vitals: ^0.2.4 => 0.2.4 workbox-background-sync: ^5.1.4 => 5.1.4 workbox-broadcast-update: ^5.1.4 => 5.1.4 workbox-cacheable-response: ^5.1.4 => 5.1.4 workbox-core: ^5.1.4 => 5.1.4 workbox-expiration: ^5.1.4 => 5.1.4 workbox-google-analytics: ^5.1.4 => 5.1.4 workbox-navigation-preload: ^5.1.4 => 5.1.4 workbox-precaching: ^5.1.4 => 5.1.4 workbox-range-requests: ^5.1.4 => 5.1.4 workbox-routing: ^5.1.4 => 5.1.4 workbox-strategies: ^5.1.4 => 5.1.4 workbox-streams: ^5.1.4 => 5.1.4 npmGlobalPackages: @aws-amplify/cli: 5.5.0 modernizr: 3.11.3 netlify-cli: 2.59.1 nodemon: 2.0.4 npm: 6.14.4 ```

Describe the bug

I am looping over the keys in an object and trying to update every value associated with those keys using datastore. However, when trying to do this the object is assigned a type of never

Screen Shot 2021-10-20 at 7 38 07 PM

Expected behavior

I expected it to have the proper type since it did not give any type errors in the if statement on line 552

Reproduction steps

  1. Get object with new data
  2. Loop over object keys
  3. Use datastore.save() to try to update all the keys whose values are modified

Code Snippet

// Put your code below this line.
export const updateQuoteInfo = createAsyncThunk<
  ReduxQuoteInfo[],
  ReduxQuoteInfo[],
  {
    state: RootState
    rejectValue: string
  }
>('quote/updateQuoteInfo', async (rows, { getState, rejectWithValue }) => {
  try {
    const updatedRows = []
    const { quotes } = getState()
    const previousRows = quotes.oldQuoteInfo
    // * We are using an array to store promises of all rows that need to be updated or added
    const updateOrNewCostlinePromises = []
    for (let i = 0; i < rows.length; i++) {
      // * new row is not a field in graphql schema it just to check if a newRow has been added
      const eachRow = rows[i]
      // We are using newRows to update the redux state with the new row values, For lines that were updated we need to replace the index number with the updated or added rows
      // First we check if the redux row for quote Info is the same
      if (!isEqual(eachRow, previousRows[i])) {
        //*  We are removing version, last changed, at from the rows when comparing because version field should not be updated by user.
        const { _version: v2, _lastChangedAt: l2, ...copyUpdatedRow } = eachRow
        const updatePromise = async () => {
          const originalRow2 = await DataStore.query(QuoteInfo, eachRow.id as string)
          const parsedOriginalRow2 = JSON.parse(JSON.stringify(originalRow2)) as ReduxQuoteInfo
          console.log('originalRow2: ', parsedOriginalRow2)
          if (originalRow2) {
            const updatedRow: QuoteInfo = await DataStore.save(
              QuoteInfo.copyOf(originalRow2 as QuoteInfo, original => {
                for (const property in copyUpdatedRow) {
                  // Only updates the property that is different
                  const typedProperty = property as keyof typeof copyUpdatedRow
                  // type t2 = Omit<
                  //   ReduxQuoteInfo,
                  //   'id' | '_version' | '_deleted' | '_lastChangedAt' | 'createdAt' | 'updatedAt'
                  // >
                  const typedProperty2 = property as keyof MutableModel<QuoteInfo>
                  // a = original[typedProperty2]
                  if (!isEqual(copyUpdatedRow[typedProperty], original[typedProperty2])) {
                    original[typedProperty2] = copyUpdatedRow[typedProperty]
                  }
                }
              })
            )
            // console.log('updatedRow2: ', updatedRow2)
            return updatedRow
          }
          // updatedRows.push(updatedRow)
        }
        updateOrNewCostlinePromises.push(updatePromise())
      } else {
        updatedRows.push(eachRow)
      }
    }
    // All promise are executed at once
    await Promise.all(updateOrNewCostlinePromises)

    return rows
  } catch (err: any) {
    makeErrorToast(msg)
    return rejectWithValue(err)
  }
})

Log output

``` // Put your logs below this line ```

aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

jimblanc commented 1 year ago

We have published an RFC on our plan for improving TypeScript support in Amplify JS & would love to get your feedback & suggestions!

RFC: Amplify JS TypeScript Improvements

david-mcafee commented 1 year ago

@ShreyasBhandari - if I'm reading your code snippet correctly, the following would update the original with the new fields without TypeScript errors:

const updatedRow: QuoteInfo = await DataStore.save(
    QuoteInfo.copyOf(originalRow2, (original) => {
        Object.assign(original, copyUpdatedRow);
    })
);

If that doesn't work, let me know! Thank you!

david-mcafee commented 1 year ago

@ShreyasBhandari - closing this ticket as we have not heard back from you. If the guidance I provided above does not resolve your issue, please let us know. Thanks!