Typescript - My property decorator isn't working, why? - node.js

I'm trying to construct custom decorators, for example, a decorator to validate the minimum length of a string.
function Min(limit: number) {
return function (target: Object, propertyKey: string) {
let value: string;
const getter = function () {
return value;
};
const setter = function (newVal: string) {
if (newVal.length < limit) {
Object.defineProperty(target, 'errors', {
value: `Your password should be bigger than ${limit}`
});
}
else {
value = newVal;
}
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter
});
}
}
In the class, I'm calling this way:
export class User {
#Min(8)
password: string;
}
However, I'm getting this exception:
tslib_1.__decorate([
^
ReferenceError: Min is not defined
at Object.<anonymous>
My tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true
},
"exclude": [
"node_modules",
"dist"
]
}
PS: I know there are libraries, for data validations, like the class-validator, however, I would like to create custom decorators for validations and other features.
Where am I going wrong?

It looks like you forgot to import your decorator. Assuming your structure as following:
- decorator.ts
- index.ts
In decorator.ts:
export function Min(limit: number) {
// ...
}
In the index.ts:
// Don't forget to import your decorator here
import { Min } from './decorator';
export class User {
#Min(8)
password: string;
}

Related

Unable to resolve signature of class decorator when called as an expression when using class and method decorators

i have user class and im attempting to declare a class and method decorator. However im getting the following errors:
error TS1238: Unable to resolve signature of class decorator when
called as an expression. This expression is not callable.
Type 'void' has no call signatures.
1 #OpenAPIPaths({ path: "Users" })
error TS1241: Unable to resolve signature of method decorator when
called as an expression. This expression is not callable.
Type 'void' has no call signatures.
7 #OpenAPIFunction({ Request: {}, Response: {} })
user.controller.ts
#OpenAPIPaths({ path: "Users" })
export class UserController {
constructor() {
console.log("init");
}
#OpenAPIFunction({ Request: {}, Response: {} })
getUser(id: string) {
return id;
}
}
function OpenAPIPaths(other: any): void {
console.log("class", other);
}
function OpenAPIFunction(here: any): void {
console.log("function", here);
}
tsconfig.js
{
"extends": "./tsconfig.paths.json",
"compilerOptions": {
"lib": ["ESNext"],
"moduleResolution": "node",
"noUnusedLocals": false,
"noUnusedParameters": false,
"removeComments": true,
"sourceMap": true,
"target": "ES2020",
"outDir": "dist",
"esModuleInterop": true,
"strictNullChecks": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true
},
"include": ["./**/*.ts"],
"exclude": ["node_modules/**/*", ".serverless/**/*", ".webpack/**/*", "_warmup/**/*", ".vscode/**/*"],
"ts-node": {
"require": ["tsconfig-paths/register"]
}
}
node:16
typescript: 4.9.3

How to use the Lit-Element query decorator?

Code fails Typescript compilation when attempting to use the Lit-Element query decorator.
With tsconfig.json
{
"compilerOptions": {
"target": "es2017",
"module": "es2015",
"moduleResolution": "node",
"lib": ["es2017", "dom"],
"outDir": "../../out/client",
"allowJs": true,
"experimentalDecorators": true
}
}
and package.json
"dependencies": {
"lit-element": "^2.2.1"
},
tsc command yields the following error:
Unable to resolve signature of property decorator when called as an expression.
Type '{ kind: string; placement: string; key: any; descriptor: any; }' is not assignable to type 'void'.
However, if I comment out the two lines of the decorator, and un-comment the equivalent get function, no errors.
#customElement("my-app")
export default class extends LitElement {
#query("p")
p: any;
// get p() {
// return this.shadowRoot.querySelector("p");
// }
render() {
return html`
Hello World!
<p>A paragraph</p>
`;
}
I expected identical behavior using either the query decorated property or the property getter.
Do you correctly import the query annotation?
The following element works for me:
/**
* Import LitElement base class, html helper function,
* and TypeScript decorators
**/
import {
LitElement, html, customElement, query
} from 'lit-element';
#customElement("my-app")
export default class extends LitElement {
#query("p")
p: any;
// get p() {
// return this.shadowRoot.querySelector("p");
// }
render() {
return html`
Hello World!
<p>A paragraph</p>
`;
}
}
With a tsconfig.json:
{
"compilerOptions": {
"moduleResolution": "node",
"module": "es6",
"target": "es6",
"experimentalDecorators": true
}
}

Rockset -- Could not find a module declaration for module './rest/index'. ../../rest/index.js has implicitly type any

I have been looking through all of the other posts about the same issues in react/typescript projects but none of these have worked for me...not even changing noImplicitAny to false and strict to false is ts.config or setting the "excludes" and "typeRoots".
I've tried declaring a module, I've tried putting it in a .d.ts file, I've tried using #ts-ignore, I can't get anything to work. It's driving me crazy...can somebody tell me how to resolve this once and for all so I can get this to compile properly?
There are a whole lot more issues of the same type in the index.js file but once with a ton of requires throwing the same issue but once I can resolve this, I should be able to fix the others as well...
(function(factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['rest/index'], factory);
} else if (typeof module === 'object' && module.exports) {
// CommonJS-like environments that support module.exports, like Node.
module.exports = factory(require('./rest/index')); **<---- ERROR HERE**
}
}(function(restIndex) {
return restIndex;
}));
EDIT:
Client.js:
var apiKey = process.env.ROCKSET_APIKEY;
var apiServer = process.env.ROCKSET_APISERVER;
var rockset = require('rockset')(apiKey, apiServer);
var getResponseLogger = function(callback) {
return function(error, response, body) {
if(error) {
console.log(error.response.error.text);
callback();
} else {
console.log(response);
callback();
}
}
}
export function getMaxSkill() {
rockset.queries.query({
'sql': {
'query': 'select * from "testCollection"'
}
}, null, getResponseLogger(done))
}
function done() {
console.log('\n\n===done===');
}
App.tsx:
import {getMaxSkill} from './client';
async componentDidMount() {
getMaxSkill();
}
tsconfig
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"typeRoots": [
"../../#types",
"../../node_modules/#types"
]
},
"include": [
"src", "tests/App.test.js"
],
"paths": {
"#material-ui/core": ["./material-ui/src"],
"#material-ui/core/*": ["./material-ui/src/*"],
"#material-ui/lab": ["./material-ui-lab/src"],
"#material-ui/lab/*": ["./material-ui-lab/src/*"],
"#material-ui/styles": ["./material-ui-styles/src"],
"#material-ui/styles/*": ["./material-ui-styles/src/*"],
"#material-ui/system": ["./material-ui-system/src"],
"#material-ui/types": ["./material-ui-types"]
}
}

TypeDI #Inject() doesn't work, but Container.get() does

I'm facing weird issue,
Container.get(MyServiceName); returns demanded service, yet class property decorated with #Inject() is undefined.
I've tried decorate class with #Service()
I do import reflect-metada at entry point of my application
import 'reflect-metadata';
I'm wondering if it may be related to fact, that that I'm using typedi not directly from my node_modules but from my dependency?
Application doesn't use Express framework neither routing controllers
My tsconfig.json:
"compilerOptions": {
"declaration": true,
"pretty": true,
"esModuleInterop": true,
"target": "esnext",
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": false,
"moduleResolution": "node",
"noUnusedParameters": true,
"strictPropertyInitialization": false,
"module": "commonjs",
"lib": ["dom", "es2018"],
"importHelpers": true,
"outDir": "./dist",
"strict": true,
"typeRoots": ["node_modules/#types"],
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"sourceMap": true
},
"include": ["./src/**/*"],
"exclude": ["node_modules", "./dist/**/*"]
}
Service that I want to be injected
import MyServiceName from '../services/MyServiceName';
import { Container, Inject, Service } from 'my_dependency/lib';
export default class SomeClass{
#Inject()
private readonly someService: MyServiceName;
public async someMethod(): SomeResponseType {
const thatWorks = Container.get<MyServiceName>(MyServiceName);
console.log(this.someService); // undefined
console.log(thatWorks); // proper class
}
}
Service I want to inject
#Service()
export default class MyServiceName {
public async someMethod(): SomeReturnType {
return someReturnedData;
}
}
I would like to inject my dependencies though #Inject() decorator
You need to create your instance of SomeClass using Container#get, or the container won't be able to inject the property.
This worked for me:
// a.ts
import { Service } from "typedi";
#Service()
export class Foo {
foo() {
return 10;
}
}
// b.ts
import { Foo } from "./a";
import { Inject } from "typedi";
export class Bar {
#Inject() private readonly service: Foo
foo() {
console.log(this.service);
}
}
// test.ts
import "reflect-metadata";
import { Bar } from "./b";
import { Container } from "typedi";
const noFoo = new Bar();
noFoo.foo(); // yields undefined
const withFoo = Container.get<Bar>(Bar);
withFoo.foo(); // yields Foo

TypeScript ES dynamic `import()`

While using the new TypeScript feature, so called ES Dynamic Imports, I am not able to run the code of my isomorphic app on the server side using ts-node.
It seems like the error does not occur when using the webpack module loader which transpiles the code in it's own way and running resulting files in a browser.
The error which I've got:
case 0: return [4 /*yield*/, import("./component/main")];
^^^^^^
SyntaxError: Unexpected token import
Usually TypeScript transpiles the import expression to something like that: Promise.resolve(require("./component/main")), but I can't see it there.
How to fix that? Does it have something common with ts-node? Or there is a "polyfill" for node.js?
My tsconfig.json file:
{
"compilerOptions": {
"declaration": false,
"emitDecoratorMetadata": true,
"allowJs": false,
"experimentalDecorators": true,
"importHelpers": true,
"inlineSourceMap": false,
"inlineSources": false,
"lib": [
"DOM",
"ES5",
"ES6",
"ES7"
],
"listFiles": false,
"module": "commonjs",
"noEmitOnError": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"preserveConstEnums": false,
"pretty": false,
"removeComments": false,
"strict": true,
"target": "es5"
}
}
the code:
import * as m from "mithril";
import LayoutComponent from "./component/layout";
const render = (
layout: m.ComponentTypes<any, any>,
) => ({ tag, attrs }: m.Vnode<any, any>) => m(layout, attrs, m(tag as any, attrs));
export default {
"/:path...": {
onmatch: async (args, path) => (await import("./component/main")).default,
render: render(LayoutComponent),
},
} as m.RouteDefs;
This is a bug in the Typescript Compiler which will be fixed in 2.5.
Exporting a default object with a function that imports a file will not compile the import statement into a require statement in Typescript 2.4.x.
For example, while this:
export const sudo = { run() { return import('./test3'); } }
Will compile to this:
exports.sudo = { run: function () { return Promise.resolve().then(function () { return require('./test3'); }); } };
This:
export default { run() { return import('./test3'); } }
Compiles into this:
exports.default = { run: function () { return import('./test3'); } };
Which is obviously wrong. A temporary solution would be this:
export const sudo = { run() { return import('./test3'); } }
export default sudo;
Which compiles (correctly) into this:
exports.sudo = { run: function () { return Promise.resolve().then(function () { return require('./test3'); }); } };
exports.default = exports.sudo;

Resources