Amplify-js: TypeScript types are incorrect

Created on 16 Feb 2020  路  10Comments  路  Source: aws-amplify/amplify-js

Describe the bug
I am calling Auth.currentAuthenticatedUser() to get the current users data, attributes, and sub. The function states that it returns a CognitoUser, which I import like this import { CognitoUser } from '@aws-amplify/auth'. And it says that CognitoUser does not have an attributes property, but if I console.log the result from Auth.currentAuthenticatedUser() I can clearly see an attributes property. On CognitoUser there is a method getUserAttributes but it is async and receives a callback which is very weird.
Are the types wrong? Or I shouldn't access the attributes directly and through the function that it provides?

To Reproduce

  1. import { Auth } from 'aws-amplify'
  2. import { CognitoUser } from '@aws-amplify/auth'
  3. const user: CognitoUser = await Auth.currentAuthenticatedUser()
  4. try to access user.attributes.something
  5. Get TypeScript error

Expected behavior
TypeScript types include all of the properties that are returned from the specific function.

Auth Cognito TypeScript needs-discussion

Most helpful comment

Yes, the types in Cognito are messed up. They don't match actual response objects and thus if you use them your app breaks but if you don't use them, then passing the cognito user around into other Cognito methods breaks things too. It's quite a mess.

All 10 comments

@idanlo Can you provide the TypeScript error you are receiving? Also, can you provide a code snippet of your code along with the package.json, this helps us with understanding what is currently being used.

@sammartinez The error I'm getting - Property 'attributes' does not exist on type 'CognitoUser'.ts(2339).
The code snippet exists in the reproduction I gave, as simple as that. version - aws-amplify: "^2.2.5"

Yes, the types in Cognito are messed up. They don't match actual response objects and thus if you use them your app breaks but if you don't use them, then passing the cognito user around into other Cognito methods breaks things too. It's quite a mess.

The way I did to overcome this problem (at least temporary) was to create a new custom type like that extends the base CognitoUser type like following:

type MyCognitoAttributes = { 'custom:xxx': string; ... }

type MyCognitoUser = CognitoUser & { attributes: MyCognitoAttributes; }

This way I am covering my custom attributes also

Here's my temporary fix, which also extends the CognitoUser type, but does so via a Typescript interface that extends the CognitoUser type:

[ Note: ...but first, I define a custom UserAttributes type to only have the fields used in my app, since those could be anything. ]

import { CognitoUser } from '@aws-amplify/auth';

...

/*
 * Custom attributes type defined according to the attributes used in this app
 */
export interface UserAttributes {
    sub: string;
    email: string;
    email_verified: string;
    name: string;
    updated_at: string;
    'custom:bytesQuota': string;
    'custom:bytesUsed': string;
}

...

/*
 * The following interface extends the CognitoUser type because it has issues
 * (see github.com/aws-amplify/amplify-js/issues/4927). Eventually (when you
 * no longer get an error accessing a CognitoUser's 'attribute' property) you
 * will be able to use the CognitoUser type instead of CognitoUserExt.
 */
interface CognitoUserExt extends CognitoUser {
    attributes: UserAttributes;
}

...

// usage, in the code:

        this.user = await Auth.currentAuthenticatedUser();
        return this.user ? this.user.attributes : null;

Just noting that a similar thing occurs with challengeName (as has been noted) and was brought up almost a year ago but then closed (#3733).

Just noting that a similar thing occurs with challengeName (as has been noted) and was brought up almost a year ago but then closed (#3733).

Same here. 'Property 'challengeName' does not exist on type 'CognitoUser'.Vetur(2339)
The types need to be fixed.

There is a workaround: https://github.com/aws-amplify/amplify-js/issues/3733#issuecomment-533817764

Bump. Any updates on this, Amplify team?

Meanwhile I was able to work around following some internal code [1] and [2].

Basically importing import { CognitoUserInterface } from '@aws-amplify/ui-components'; and using it to declare variables and also for type assertions as CognitoUserInterface.

e.g.

const AuthStateApp: React.FunctionComponent = () => {
  const [authState, setAuthState] = React.useState<AuthState>();
  const [user, setUser] = React.useState<CognitoUserInterface | undefined>();

  React.useEffect(() => {
    return onAuthUIStateChange((nextAuthState, authData) => {
      setAuthState(nextAuthState);
      setUser(authData as CognitoUserInterface);
    });
  }, []);

  return authState === AuthState.SignedIn && user ? (
    <div className="App">
      <div>Hello, {user.username}</div>
      <AmplifySignOut />
    </div>
  ) : (
      <AmplifyAuthenticator />
    );
}

I noticed that when I log the return value, it's the class instance of CongnitoUser, and you can call the class methods on it like such.
if (response && response instanceof CognitoUser) { return <div>{response.getUsername()}</div>; }

Was this page helpful?
0 / 5 - 0 ratings

Related issues

martimarkov picture martimarkov  路  74Comments

ghost picture ghost  路  84Comments

nomadus picture nomadus  路  57Comments

CodySwannGT picture CodySwannGT  路  69Comments

guanzo picture guanzo  路  63Comments