ember-arg-types
v1.1.0
Published
Runtime type checking & defaulting for glimmer component arguments powered by prop-types & decorators
Downloads
28,078
Readme
ember-arg-types
API
@arg
Property decorator for declaring for glimmer component argument runtime type checks and default values, powered by facebook/prop-types.
@forbidExtraArgs
Class decorator for checking that only arguments with the @arg
decorator are provided to a component (e.g. prevent misspelled or invalid arguments).
Motivation
ember-arg-types
provides an @arg
decorator that maps glimmer arguments to local component properties. This allows default values and type checking to be easily declared (and documented) in your component JS file.
Example:
@arg(string)
sortBy = 'id';
Instead of this:
get sortBy() {
const { sortBy = 'id' } = this.args;
assert('`sortBy` must be a string', typeof sortBy === 'string');
return sortBy;
}
It also provides an opt-in class decorator @forbidExtraArgs
that will verify that all arguments passed to the component have been registered with the @arg
. This allows you to catch easy mistakes such as misspelled or invalid arguments.
import Component from '@glimmer/component';
import { arg, forbidExtraArgs } from 'ember-arg-types';
import { string } from 'prop-types';
@forbidExtraArgs
export default class ExampleComponent extends Component {
@arg(string)
hardToRememberArgument;
}
Argument Mapping & Default Values
The @arg
decorator maps this.args
values to local component properties. If a mapped argument has a value of undefined
, @arg
will return the local property's initializer value.
Here the value of this.sortBy
is the value of this.args.sortBy
, unless this.args.sortBy
is undefined
. If undefined
, the value of this.sortBy
will be 'id'
.
@arg
sortBy = 'id';
Here the value of this.id
is the value of this.args.id
, unless this.args.id
is undefined
. If undefined
, the value of this.id
will be computed by the getter.
@arg
get id() {
return guidFor(this);
}
Type Checking
ember-arg-types
uses the popular prop-types library for runtime type checking.
By importing type validators from prop-types, you can specify a type check parameter to @arg
:
import Component from '@glimmer/component';
import { arg, forbidExtraArgs } from 'ember-arg-types';
import { string } from 'prop-types';
@forbidExtraArgs
export default class CharacterComponent extends Component {
// `name` string arg that is required
@arg(string.isRequired)
name;
}
Example Type Check Error
{{! @name should be a string, not a number }}
<Character @name={{123}} />
Example Extra Argument Error
{{! @numHeart should be @hearts }}
<ExtendedCharacter @name='character' @numHeart={{3}} />
Prop Type Docs
You can find more information on prop-type
validators here: Prop Type Usage Docs
Disable Errors
If an argument value fails a validation check, an Error
will be thrown (in non-prod environments) by default. To disable throwing Error
s , update your config/environment.js
with the following:
'ember-arg-types': {
// Throw errors instead of logging (default is true)
throwErrors: false
}
Production
Since component type checks are not typically performed in production, prop-types replaces the library with function shims for production builds, resulting in a smaller bundle size.
Installation
ember install ember-arg-types
Usage
Example Component Definition
// components/character.js
import Component from '@glimmer/component';
import { arg, forbidExtraArgs } from 'ember-arg-types';
import { func, number, oneOf, string } from 'prop-types';
import { guidFor } from '@ember/object/internals';
const tunics = ['green', 'red', 'blue'];
@forbidExtraArgs // Asserts only @arg arguments are provided
export default class CharacterComponent extends Component {
// `id` string arg with a getter default value
@arg(string)
get id() {
return guidFor(this);
}
// `name` string arg that is required
@arg(string.isRequired)
name;
// `title` arg with default value and no type check
@arg
title = 'hero';
// `tunic` arg with set of valid string values and a default
@arg(oneOf(tunics))
tunic = tunics[0];
// `hearts` number arg with default value
@arg(number)
hearts = 12;
// `level` number arg without default value
@arg(number)
level;
// `onClick` action (function) arg with noop default value
@arg(func)
onClick = () => null;
}
{{! components/character.hbs }}
{{! args are mapped to local properties, thus we use this.<argName> instead of @<argName> }}
<div class='character' role='button' {{on 'click' this.onClick}}>
<div class='id'>{{this.id}}</div>
<div class='name'>{{this.name}}</div>
<div class='title'>{{this.title}}</div>
<div class='tunic'>{{this.tunic}}</div>
<div class='hearts'>{{this.hearts}}</div>
<div class='level'>{{this.level}}</div>
</div>
Example Component Invocation
<Character
@name='link'
@title='hero of time'
@level={{2}}
@onClick={{this.onClick}}
@heart={{5}}
{{! Should be '@hearts' will catch because of @forbidExtraArgs }}
/>
Compatibility
- Ember.js v3.24 or above
- Ember CLI v3.24 or above
- Node.js v14 or above
- ember-auto-import v2
Contributing
See the Contributing guide for details.
License
This project is licensed under the MIT License.