Необработанное promiserejectionwarning: ошибка типа: не удается прочитать свойство ‘type’ неопределенного

#typescript #express #graphql #apollo-server #typegraphql

#typescript #выразить #graphql #apollo-сервер #typegraphql

Вопрос:

Я использую type-graphql с apollo-server и пытаюсь обрабатывать ошибки, используя типы объединения, например, я хочу вернуть ошибку GQLError (пользовательский тип), когда что-то идет не так в запросе / мутации. Мой код для типов распознавателя, сущности и пользовательского объединения:

user/entity.ts :

 import { 
  BaseEntity,
  Entity,
  PrimaryColumn,
  Column,
  CreateDateColumn,
  UpdateDateColumn,
  BeforeInsert
} from "typeorm";
import { ObjectType, Field, ID } from "type-graphql";
import { v4 as uuid } from "uuid";
import * as bcrypt from "bcrypt";

@Entity("users")    
@ObjectType()
export class User extends BaseEntity {
  @PrimaryColumn("uuid")
  @Field(() => ID)
  id: string;
  
  @Column("varchar", { length: 255, unique: true })
  @Field()
  username: string;
    
  @Column("varchar", { length: 255 })
  @Field()
  password: string;    
  
  @Column("varchar", { length: 255, nullable: true })
  @Field()
  email?: string;

  @CreateDateColumn()
  created: Date;

  @UpdateDateColumn()
  updated: Date;

  @BeforeInsert()
  async setup(): Promise<void> {
    this.id = uuid();
    this.password = await bcrypt.hash(this.password, bcrypt.genSaltSync(12));
  }
}
  

user/types.ts

 import { createUnionType } from "type-graphql";             
                                                            
import { GQLErrorResponse } from "../shared/index.entity";  
import { User } from "./entity";                            
                                                            
export const RegistrationResponse = createUnionType({       
  name: "UserRegistrationResponse",                         
  types: () => [User, GQLErrorResponse] as const            
});                                                         
                                                            
export const LoginResponse = createUnionType({              
  name: "UserLoginResponse",                                
  types: () => [User, GQLErrorResponse] as const            
});                                                         
                                                            
export const UserQueryResponse = createUnionType({          
  name: "UserQueryResponse",                                
  types: () => [User, GQLErrorResponse] as const,           
  resolveType: (value: User | GQLErrorResponse) => {        
    if ("id" in value) {                                    
      return User;                                          
    } else {                                                
      return GQLErrorResponse;                              
    }                                                       
  }                                                         
});                                                         
                                                            
export const UsersQueryResponse = createUnionType({         
  name: "UsersQueryResponse",                               
  types: () => [User, GQLErrorResponse] as const            
});                                                         
  

user/resolver.ts

 import { Resolver, Arg, Query, Mutation } from "type-graphql";
import * as bcrypt from "bcrypt";
import * as _ from "lodash";

import { User } from "./entity";
import { UserRegistrationInput, UserLoginInput } from "./inputs";
import { UserQueryResponse, UsersQueryResponse } from "./types";

@Resolver(User)
export class UserResolver {
  @Query(() => [User])
  async users(): Promise<User[]> {
    return User.find({});
  }

  @Query(() => UserQueryResponse)
  async user(
    @Arg("id", { nullable: true }) id?: string
  ): Promise<typeof UserQueryResponse> {
    const user: User | undefined = await User.findOne(id);
    if (_.isEmpty(user)) {
      return {
        errors: [
          {
            path: "id",
            message: "User not found"
          }
        ]
      };
    }
    return user as User;
  }

  @Mutation(() => User)
  async register(@Arg("input") input: UserRegistrationInput): Promise<User> {
    const user: User = await User.create(input).save();

    return user;
  }

  @Mutation(() => User)
  async login(@Arg("input") input: UserLoginInput): Promise<User> {
    const user: User | undefined = await User.findOne({
      where: { username: input.username }
    });
    const valid: boolean = await bcrypt.compare(input.password, user.password);
    if (!valid) {
      throw new Error("Invalid username/password");
    }
                                                                                                          
    return user;                                                                                          
  }                                                                                                       
}                                                                                                         
  

Однако, когда я запускаю свой код, я получаю следующую ошибку:

 (node:325229) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'type' of undefined
  

Ответ №1:

Я обнаружил, что проблема связана с циклическими зависимостями, а точнее: неправильным порядком импорта.

Старый ответ:

Это всего лишь предположение, потому что для меня это была проблема: вы пытались изменить тип id полей с ID на Int ?

В любом случае, в моем случае я обнаружил причину проблемы, когда изменил код type-graphql в строке, указанной в ошибке:

узел: 28896) UnhandledPromiseRejectionWarning: ошибка типа: не удается прочитать свойство «тип» неопределенного в interfaceClasses.map.InterfaceClass (/workspaces/assistant-private/node_modules/type-graphql/dist/schema/schema-generator.js:164:149)

Итак, я пошел к этому schema-generator.js и нашел это:

 types: () => unionClassTypes.map(objectType => this.objectTypesInfo.find(type => type.target === objectType).type),
  

Оказалось, это objectType уже undefined было, поэтому я изменил его на это:

 types: () => unionClassTypes.filter(a => a).map(objectType => this.objectTypesInfo.find(type => type.target === objectType).type),
  

После этого я получил следующую ошибку вместо TypeError: Cannot read property 'type' of undefined :

Ошибка GraphQL: поле интерфейса IBaseEntity.id ожидает тип Int! но BaseAction.id это тип Float!.