Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InvalidTypeError: Type is invalid! Type is: "undefined" #326

Closed
winterNebs opened this issue Jul 16, 2020 · 15 comments
Closed

InvalidTypeError: Type is invalid! Type is: "undefined" #326

winterNebs opened this issue Jul 16, 2020 · 15 comments
Labels
question Qustions about how to use stuff

Comments

@winterNebs
Copy link

Versions

  • typegoose: 7.3.0
  • mongoose: 5.9.24
'''

Using the following definition for UserClass:

@pre<UserClass>("save", async function (next: HookNextFunction): Promise<void> {
    ...
})
@index({ name: 1 }, { collation: { locale: "en", strength: 1 }, unique: true })
export class UserClass {
    @prop({
        required: true,
        trim: true,
        unique: true,
        minlength: 3,
        maxlength: 20
    })
    public name!: string;
...
}

I get the following unhelpful error:

@typegoose\typegoose\lib\internal\processProp.js:160
        throw new errors_1.InvalidTypeError(name, key, Type);
        ^

InvalidTypeError: "UserClass.name"'s Type is invalid! Type is: "undefined"
    at Object.processProp (C:\Users\winterNebs\source\repos\AscensionGame\AscensionGame\node_modules\@typegoose\typegoose\lib\internal\processProp.js:160:15)
    at Object._buildSchema (C:\Users\winterNebs\source\repos\AscensionGame\AscensionGame\node_modules\@typegoose\typegoose\lib\internal\schema.js:37:27)
    at buildSchema (C:\Users\winterNebs\source\repos\AscensionGame\AscensionGame\node_modules\@typegoose\typegoose\lib\typegoose.js:120:20)
    at Object.getModelForClass (C:\Users\winterNebs\source\repos\AscensionGame\AscensionGame\node_modules\@typegoose\typegoose\lib\typegoose.js:70:39)
    at Object../database/schema/user.ts (C:\Users\winterNebs\source\repos\AscensionGame\AscensionGame\dist\app.js:2121:28)
    at __webpack_require__ (C:\Users\winterNebs\source\repos\AscensionGame\AscensionGame\dist\app.js:20:30)
    at Object../gameAppServer.ts (C:\Users\winterNebs\source\repos\AscensionGame\AscensionGame\dist\app.js:2409:16)
    at __webpack_require__ (C:\Users\winterNebs\source\repos\AscensionGame\AscensionGame\dist\app.js:20:30)
    at Object../app.ts (C:\Users\winterNebs\source\repos\AscensionGame\AscensionGame\dist\app.js:1351:25)
    at __webpack_require__ (C:\Users\winterNebs\source\repos\AscensionGame\AscensionGame\dist\app.js:20:30)
    at C:\Users\winterNebs\source\repos\AscensionGame\AscensionGame\dist\app.js:84:18
    at Object.<anonymous> (C:\Users\winterNebs\source\repos\AscensionGame\AscensionGame\dist\app.js:87:10)
    at Module._compile (internal/modules/cjs/loader.js:955:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:991:10)
    at Module.load (internal/modules/cjs/loader.js:811:32)
    at Function.Module._load (internal/modules/cjs/loader.js:723:14)

As far as I can tell the type for name is string, not undefined.
Can anyone spot what I did wrong or is this error not related to this code?

I migrated from ts-mongoose since that project doesn't look like its being updated anymore, so sorry that I don't have a very minimal example.

@winterNebs winterNebs added the question Qustions about how to use stuff label Jul 16, 2020
@JohnDeved
Copy link
Contributor

encountering the same issue right now.
setting "emitDecoratorMetadata": true in the tsconfig.json fixed it for me

@hasezoey
Copy link
Member

@winterNebs there dosnt seem to be anything wrong with the provided code, please confirm the following:

@winterNebs
Copy link
Author

Shoot, that's my bad guys. I had the experimentalDecorators set but I missed the metadata option as well.

Thanks for the quick responses

@josh-feldman
Copy link

I am seeing this same error if module in tsconfig is set to es6 instead of commonjs. All requirements are set properly and dependencies are met. Is module = commonjs required?

If I use commonjs it works fine, but I would prefer to compile to es6 for the rest of the project if possible. This should be listed as a requirement if it is actually required.

@hasezoey
Copy link
Member

hasezoey commented Aug 1, 2020

@josh-feldman from what i know, this shouldnt matter, there is already an issue about shipping esm #214

and without the full error, i cannot say for sure what is happening

@josh-feldman
Copy link

josh-feldman commented Aug 1, 2020

@hasezoey My apologies, some detail:

"mongoose": "5.9.27"
"typescript": "3.9.7"
"@typegoose/typegoose": "7.3.1"

Error:

/package/node_modules/@typegoose/typegoose/lib/internal/processProp.js:160
        throw new errors_1.InvalidTypeError(name, key, Type);
              ^
/package/node_modules/@typegoose/typegoose/lib/internal/processProp.js:1
Error: "User.userId"'s Type is invalid! Type is: "undefined"
    at Object.processProp (/package/node_modules/@typegoose/typegoose/lib/internal/processProp.js:160:15)
    at Object._buildSchema (/package/node_modules/@typegoose/typegoose/lib/internal/schema.js:37:27)
    at buildSchema (/package/node_modules/@typegoose/typegoose/lib/typegoose.js:120:20)
    at getModelForClass (/package/node_modules/@typegoose/typegoose/lib/typegoose.js:70:39)
    at Object.<anonymous> (/package/dist/models/UserModel.js:21:26)
    at Generator.next (<anonymous>)
    at Object.<anonymous> (/package/dist/app.js:1)
    at Generator.next (<anonymous>)

User Model.ts

import { prop, modelOptions, getModelForClass, DocumentType } from '@typegoose/typegoose';

@modelOptions({ schemaOptions: { timestamps: true, strict: 'throw', collection: 'user' } })
export class User {
  @prop({ required: true, index: true })
  userId!: number;
}

export type UserDocument = DocumentType<User>;
export const UserModel = getModelForClass(User);

tsconfig:

{
  "compilerOptions": {
    "target": "ES6",
    "lib": [
      "es6",
    ],
    "downlevelIteration": true,
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "module": "es6",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "outDir": "./dist/",
    "rootDir": "./src",
    "preserveSymlinks": true
  },
  "include": [
    "src",
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts",
  ]
}

Command to start:

# app.js is a simple script that connects to mongo and imports UserModel
tsc --project ./tsconfig.json && node -r esm dist/app.js

@hasezoey
Copy link
Member

hasezoey commented Aug 1, 2020

i tried it with esm modules (after i figured out on how to do so), and had no errors, the only problem i came across was "typegoose dosnt have an default export and commonjs dosnt support named exports" (which i still dont quite understand on why)

@josh-feldman
Copy link

I am seeing this issue on Node 10 & use the esm package to resolve the ESM modules per the start command.

For your record, this is what the above UserModel file gets compiled down to in both cases:

module: ES6 (throws the error I mentioned above):

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { prop, modelOptions, getModelForClass } from '@typegoose/typegoose';
let User = class User {
};
__decorate([
    prop({ required: true, index: true }),
    __metadata("design:type", Number)
], User.prototype, "userId", void 0);
User = __decorate([
    modelOptions({ schemaOptions: { timestamps: true, strict: 'throw', collection: 'users' } })
], User);
export { User };
export const UserModel = getModelForClass(User);

module: commonjs works:

"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserModel = exports.User = void 0;
const typegoose_1 = require("@typegoose/typegoose");
let User = class User {
};
__decorate([
    typegoose_1.prop({ required: true, index: true }),
    __metadata("design:type", Number)
], User.prototype, "userId", void 0);
User = __decorate([
    typegoose_1.modelOptions({ schemaOptions: { timestamps: true, strict: 'throw', collection: 'users' } })
], User);
exports.User = User;
exports.UserModel = typegoose_1.getModelForClass(User);

@josh-feldman
Copy link

josh-feldman commented Aug 1, 2020

It also doesn't work if I try this workaround

UserModel.ts:

import { DocumentType } from '@typegoose/typegoose';
import * as typegoose from '@typegoose/typegoose';

// @ts-ignore
const { prop, modelOptions, getModelForClass } = typegoose.default || typegoose;
@modelOptions({ schemaOptions: { timestamps: true, strict: 'throw', collection: 'users' } })
export class User {
  @prop({ required: true, index: true })
  userId!: number;
}

export type UserDocument = DocumentType<User>;
export const UserModel = getModelForClass(User);

compiled UserModel.js:

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import * as typegoose from '@typegoose/typegoose';
// @ts-ignore
const { prop, modelOptions, getModelForClass } = typegoose.default || typegoose;
let User = class User {
};
__decorate([
    prop({ required: true, index: true }),
    __metadata("design:type", Number)
], User.prototype, "userId", void 0);
User = __decorate([
    modelOptions({ schemaOptions: { timestamps: true, strict: 'throw', collection: 'users' } })
], User);
export { User };
export const UserModel = getModelForClass(User);

It works fine if I actually provide type: Number to the prop decorator, but ideally that wouldn't be necessary

@josh-feldman
Copy link

josh-feldman commented Aug 1, 2020

I have tested converting

import { prop, modelOptions, getModelForClass } from '@typegoose/typegoose';

in the compiled output to

const { prop, modelOptions, getModelForClass } = require('@typegoose/typegoose');

and this works as well. I am thinking perhaps it has something to do with the typegoose import getting hoisted above the __metadata and __decorate references.

For now I will work around the issue with he following method.

import { DocumentType } from '@typegoose/typegoose'; // Types must still be imported

/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */
const { prop, modelOptions, getModelForClass } = require('@typegoose/typegoose'); // These must be required so they dont get hoisted

@modelOptions({ schemaOptions: { timestamps: true, strict: 'throw', collection: 'users' } })
export class User {
  @prop({ required: true, index: true })
  userId!: number;
}

export type UserDocument = DocumentType<User>;
export const UserModel = getModelForClass(User);

@JMPJNS
Copy link

JMPJNS commented Aug 7, 2020

for people using babel instead of tsc, this plugin also fixes it

@aderchox
Copy link

I have correct tsconfig with required metadata options enabled according to the manual and according to the comments here, I'm also using commonjs, however, I still get the same error for a case as simple as this:

class UsersClass {
  @prop({ required: true })
  public username!: string;

  @prop()
  public email?: string;
}

InvalidTypeError: "UsersClass.username"'s Type is invalid! Type is: "undefined" [E009]
at processProp (D:\projects\khodam\swagger\express-tsrest-react-mongo\api\node_modules@typegoose\typegoose\src\internal\processProp.ts:277:11)
at _buildSchema (D:\projects\khodam\swagger\express-tsrest-react-mongo\api\node_modules@typegoose\typegoose\src\internal\schema.ts:59:7)
at buildSchema (D:\projects\khodam\swagger\express-tsrest-react-mongo\api\node_modules@typegoose\typegoose\src\typegoose.ts:187:9)
at getModelForClass (D:\projects\khodam\swagger\express-tsrest-react-mongo\api\node_modules@typegoose\typegoose\src\typegoose.ts:102:60)
at (d:\projects\khodam\swagger\express-tsrest-react-mongo\api\src\routes\features\users\users.model.ts:14:16)
at Object. (d:\projects\khodam\swagger\express-tsrest-react-mongo\api\src\routes\features\users\users.model.ts:14:43)
at Module._compile (node:internal/modules/cjs/loader:1159:14)
at Object.F (D:\projects\khodam\swagger\express-tsrest-react-mongo\api\node_modules@esbuild-kit\cjs-loader\dist\index.js:1:941)
at Module.load (node:internal/modules/cjs/loader:1037:32)
at Module._load (node:internal/modules/cjs/loader:878:12)

@hasezoey
Copy link
Member

@aderchox if your stack-trace is anything to go by, then you are using esbuild, which does not support outputting decorator metadata because it does not have type information, see evanw/esbuild#257 (comment)

your options are:

@senher
Copy link

senher commented Jan 10, 2024

When I use it in nuxt3
"emitDecoratorMetadata": true is not effective

@hasezoey
Copy link
Member

@senher please open a new issue for your case, this issue is quite old. also from what i can tell, nuxt is using esbuild, which does not support that option unless using typescript directly nuxt/nuxt#14126, at least from my quick skim

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Qustions about how to use stuff
Projects
None yet
Development

No branches or pull requests

7 participants