Search code examples
reactjstypescriptreact-typescript

TypeError: Object(...) is not a function - function to return deep copies of objects


I'm having a TypeError: Object(...) is not a function on the following scenario:

In order to initialize the state of a component with a given Article (that will be fetched from the backend in componentDidMount) I'm doing this

// ArticlePage.tsx
import { Article, buildArticle } from "../types";

// interfaces Props and State are properly defined above
export class ArticlePage extends Component<Props, State> {
  constructor(props: Props) {
    super(props);

    this.state = {
      loading: true,
      article: buildArticle(),
      //...
    }
  }
}

In the types module, I define the type for Article, a default article object to initialize state and a function to return deep copies of the default article object:

// types/Article.ts
import { buildUser, User } from ".";

export type Article = {
  id: number;
  user: User;
  // ...
};

const articleSkeleton: Article = {
  id: 0,
  user: buildUser(),  // <------ TypeError points to this line
  //...
}
export const buildArticle = (): Article => _.cloneDeep(articleSkeleton); // _ is lodash

Same thing for User

// types/User.ts
export type User = {
  id: number;
};

const userSkeleton: User = {
  id: 0,
}
export const buildUser = (): User => _.cloneDeep(userSkeleton); // _ is lodash

Both of them under a types directory with an index.ts like this:

// types/index.ts
export * from "./Article";
export * from "./User";

If I define the articleSkeleton object like this instead of using the buildUser function

// types/Article.ts
const articleSkeleton: Article = {
  id: 0,
  user: _.cloneDeep(userSkeleton), // deep clone the object directly here instead of calling the buildUser() function to do it
  //...
}
export const buildArticle = (): Article => _.cloneDeep(articleSkeleton); // _ is lodash

it works!


But in my head those are equivalent things...

Can someone explain me why they are not, why one works and the other doesn't?
Is what I am doing a bad practice and, if so, how should I handle this?

If I'm defining a type and a function to create objects of that type, would it be better to simply create a class instead?
What would be the pros and cons of that?


Edit:

I've tried switching the order of the exports of Article and User in the index.ts file and it works now...

// types/index.ts
export * from "./User";
export * from "./Article";

Could it be that, when it was running the buildUser() function in Article, the userSkeleton object didn't exist yet?


Solution

  • It seems that the order of your constants is incorrect.

    Move the declaration of buildUser() before articleSkeleton, or alternatively use a function instead of a const.

    const buildUserOK = (): User => userSkeleton
    
    const articleSkeleton = {
      id: 0,
      userError: buildUserError(),  
      userOk: buildUserOK(),
      userOk1: buildUserFun(),
      //...
    }
    
    type User = {
      id: number;
    };
    
    const userSkeleton: User = {
      id: 0,
    }
    
    const buildUserError = (): User => userSkeleton
    
    function buildUserFun(): User { 
      return userSkeleton; 
    }
    

    Playground link