How can I require a parameter in an ember component.
E.g.:
class MyComponent extends Component {
get my_value() {
return this.args.my_argument + 1
}
}
I want the component to throw an error when the component is instantiated and not when the function is called.
The only way I can think of is like this:
class MyComponent extends Component {
constructor(owner, args) {
super(owner, args)
assert(!!args.my_argument, "MyComponent requires my_argument")
}
...
}
This however seems a bit tedious and does not look right :/
EDIT:
It would be even better if I could specify the type of the argument.
I would say that you are on the right track with your assert. If you want to make sure that it is of the right type as well you can always assert with a typeof.
class MyComponent extends Component {
constructor(owner, args) {
super(owner, args)
assert("MyComponent requires my_argument", typeof args.my_argument === "string")
}
...
}
What to remember however is that asserts are only available during development builds, it is removed from any production builds. So if you need this check in your production build you'll have to write your own assertion method.
Assertions are removed from production builds, so they can be freely added for documentation and debugging purposes without worries of incuring any performance penalty. However, because of that, they should not be used for checks that could reasonably fail during normal usage.
https://api.emberjs.com/ember/release/functions/#ember%2Fdebug/assert
Related
In my ES6 Node.js application, I have a file with a bunch of custom error classes, i.e.
class FirstCustomError extends Error {
constructor(message) {
super(message);
this.name = "FirstCustomError";
}
}
class SecondCustomError extends Error {
constructor(message) {
super(message);
this.name = "SecondCustomError";
}
}
// ...
How can I make these classes available everywhere in the application? Ideally, I don't have to import them in every file I need them in, and I don't have to resort to throw new global.errors.FirstCustomError('error message').
That is, ideally, I can just do throw new FirstCustomError('error message').
You can indeed just put them on the global object:
global.FirstCustomError = class extends Error {
constructor(message) {
super(message);
this.name = "FirstCustomError";
}
};
global.SecondCustomError = class extends Error {
constructor(message) {
super(message);
this.name = "SecondCustomError";
}
};
You would need to ensure that this happens before any other module is using them, i.e. you'd put it in a module that is required once at the beginning of your main script. An even better (more reusable) approach would be to export them as normal from your errors module, and then do
Object.assign(global, require('./errors.js'));
in your main script to install all exports as globals.
Disclaimer: Of course, this is a horrible practice! Don't do that! Just do explicitly import them everywhere you need them. It's a breeze with ES6 module syntax, and it makes everything much clearer. Also it is unlikely that you have so many custom errors in your application that are of truly cross-cutting concerns and not tied to specific functionality, in which case you shouldn't put them all in the same module anyway.
You can add them to the first js file that runs, like app.js, and make sure they are in the global scope so they will be reusable from everywhere, but it's essential they are run first so they exist when some other code tries to call them
global.customErrFunc = class FirstCustomError extends Error {
constructor(message) {
super(message);
this.name = "FirstCustomError";
}
}
throw new customErrFunc('Custom error')
Normally to have a class accessible you need to export it like this:
exports.NameOfFunction = async (req, resp) =>{..... your code};
The async is not required! just if you need to wait for a full response of a Promise
i hope this helps!
What you are trying to achieve is possible as suggested by other posters, but it is not recommended. An ideal way would be to export them into a module and import the module where ever you need it.
customErrors.js
export.modules.FirstCustomError = FirstCustomError
export.modules.SecondCustomError = SecondCustomError
I know this isn't what you are looking for, but opinionated development is good sometimes. global assignment is a very frowned upon practice while working in modular environments. You can use them in your code by importing.
import { FirstCustomError } from './customErrors.js'
If I have an ES6 component like so:
component OldThing extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div>{this.props.someProp}</div>;
}
}
an ES6 container like so:
const OldThingContainer = connect(mapStateToProps, mapDispatchToProps)(OldThing);
and a new typescript component like so:
component NewThing extends React.Component<Props, State> {
render() {
return <OldThingContainer someProp={someValue} />
}
}
I currently get an IntrinsicAttributes error about someProp not existing on OldThing. How do I get rid of that error without adding types/typedefs to/for OldThing and/or OldThingContainer? I tried the #augments solution on the OldThingContainer where it's defined but that doesn't seem to help. Neither did adding the comment on the component itself.
The reason I want to be able to import JS without types is that we are adding typescript to a very large, multiple years old code base and it's currently difficult to get people to write typescript components at all, let alone if they also have to type out every existing component their component wants to import.
Is there an existing elegant solution to this problem or am I going to have to go through ahead of everyone and manually type every existing JS component (hundreds) in some typedef file to get rid of/prevent these errors?
I tried your example and it seemed some issue with the type connect inferred for your component.
One quick'n'dirty fix would be to annotate your JS component with:
/**
* #type {React.ComponentType<any>}
*/
const OldThingContainer = connect(mapStateToProps, mapDispatchToProps)(
OldThing
);
One more elegant fix (but maybe not possible) would be trying to dive into the react-redux typings. The connect function requires you to supply some type arguments for it to properly work. When not supplied, those arguments are given {}, not any (because any can silently eat your types).
Comment: From the few I've worked with react+redux+typescript, lib typedefs are one of the biggest pain points. React typedefs can get really complicated. (flow just will hang up your editor with "waiting for flow server", which is even worse). As TS+Redux gets more popular and more support is added, this may get a lot better in a few months...
I created a new React Native project using react-native init and in the generated template, the main component class looks like this:
export default class App extends Component<{}> {
...
}
I don't really understand what the <{}> part means. I've never seen this before and all the examples seem to omit it. Just curious as to what its purpose is and if it's necessary.
When you are using typescript, you have to specify the type of values to be expected. This allows detecting mismatching properties during compile time and reduces the amount of errors.
So when you do Component<{}>, {} is the type for Props, your component will receive.
This is how React's Component class looks like:
If you notice, the type is <P, S>, which stands for <Props, State>.
There is another interface called ComponentClass that has a signature <P>,
which initializes a new component internally with state as any. This interface is used in ReactElement's type:
So all in all, you are defining a Component which accepts no props and but can have state of any type. This is usually done when you are not sure about you component's interactions.
Ideally a component should look like this:
interface IComponentState {
...
}
interface IComponentProps {
...
}
export class MyComponent<IComponentProps, IComponentState> extends React.Component {
...
}
This enforces consumer to pass any necessary properties and enforces you to have proper value of state.
This is either Typescript of Flow. You usually don't describe props as propTypes, but rather as interface or type. Then the type is passed to React.Component as a generic.
Type of props would be the passed type plus { children?: ReactNode }
Actually there are two generic arguments, second for State
Very useful and convenient stuff.
https://www.typescriptlang.org/docs/handbook/generics.html
https://www.typescriptlang.org/docs/handbook/react-&-webpack.html
These are flow type annotations. See https://flow.org/
You'll notice that there's a #flow comment at the top of the file, and a .flowconfig file in the root of the project.
Using flow here is optional, but it can help you catch bugs.
So I've been facing this weird issue but I'm not sure if it's a bug or it's me missing something here.
So I have a component called TestComponent and in the AppComponent I have a button when I click on it I get the name of the TestComponent by doing this TestComponent.name.
AppComponent:
import { TestComponent } from './test/test.component';
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
template: '<button (click)="showName()">Show</button>'
})
export class AppComponent {
showName(){
console.log('component name: "' + TestComponent.name + '"');
}
}
As you can see by clicking on the button I want to get the TestComponent's name which basically "TestComponent"
The problem is that this works well on development mode and if I call ng build too works fine and this is what I get in the console:
And when I call ng build --prod, to compress files and reduce their size, I get this result:
Is this normal ?!
Function name contains actual function name. If the function is minified to one-letter name, it loses its original name. It should never be relied on in applications that can possibly be minified at some point i.e. every client-side application. There may be exceptions for Node.js.
name is read-only in some browsers and and thus can't be overwritten. If a class needs identifier, it should be specified explicitly under different name:
class Foo {
static id = 'Foo';
...
}
Here is a related question.
Yes, this is normal since angular-cli/webpack and other tools are changing the class name by minifying the JavaScript code. So after minification in production build, you'll always only see one letter or something similar.
Don't use this name in your logic because it will break in production.
I just answered a related question here
To workaround mangling class names, you can create a function and check if it is equal to the class you want to test. In this case for TestComponent, you could use this:
getType(o: any): string {
if (o === TestComponent)
return 'TestComponent';
}
And calling it will print the Component name:
console.log(getType(TestComponent)); // will print 'TestComponent'
The best way I found was to use a library that renames the class at compile time, works similar to the C # nameof.
nameof<MyInterface>();
Result: "MyInterface"
https://github.com/dsherret/ts-nameof
I am experiencing a really weird behavior and can't even say which package to blame for it.
My setup: RequireJS project with the JSXTransformer and the jsx! plugin
I have an es6 class like this:
define([
'react'
], function(
React
) {
class MyComponent extends React.Component {
myMethod() {
otherObject.someMethod()._privateProp; // Yes, we need this accessing and have no influence on it
}
}
return MyComponent;
});
The transpiled output in the resulting bundle after running r.js is:
define('jsx!project/components/InputOutput',[
'react'
], function(
React
) {
var ____Class8=React.Component;for(var ____Class8____Key in ____Class8){if(____Class8.hasOwnProperty(____Class8____Key)){MyComponent[____Class8____Key]=____Class8[____Class8____Key];}}var ____SuperProtoOf____Class8=____Class8===null?null:____Class8.prototype;MyComponent.prototype=Object.create(____SuperProtoOf____Class8);MyComponent.prototype.constructor=MyComponent;MyComponent.__superConstructor__=____Class8;function MyComponent(){"use strict";if(____Class8!==null){____Class8.apply(this,arguments);}}
MyComponent.prototype.myMethod=function() {"use strict";
otherObject.someMethod().$MyComponent_privateProp;
};
return MyComponent;
});
Note how otherObject.someMethod().$MyComponent_privateProp; is written there. This obviously breaks because it is not a property on instances of MyComponent.
Add /** #preventMunge */ to the top of the file. See this GitHub issue:
Yes, sorry this is a non-standard fb-ism. For now you can work around this and toggle this feature off by putting /** #preventMunge */ at the top of your file -- but that's also a pretty big fb-ism. We should (a) turn this into a transform option (rather than a direct docblock directive) and (b) make it opt-in rather than opt-out (since it's non-standard).
For context: We munge all under-prefixed object properties on a per-module basis partly because our under-prefix convention applies to both objects and classes. Additionally, even if we wanted to lax the objects vs classes distinction, it's impossible to tell (in the general case) if a property is a reference to this since alias variables can occur (i.e. var self = this; self._stuff;).