Component
s first!yield
-only component). When using a backing class, you get a first-class experience using TypeScript! Unfortunately, we don’t yet support type-checking for templates, but we hope to build that out eventually. Don’t let that stop you, though: types in your component classes make for a great experience, so let’s dig in and see how it works in practice.count
is number
, and if we accidentally wrote something like this.count = "hello"
the compiler would give us an error.this.args
on the component instance afterward. Let’s imagine a component which just logs the names of its arguments when it is first constructed:super
instead of this._super
super
before we do anything else with this
, because in a subclass this
is set up by running the superclass's constructor first (as implied by the JavaScript spec)super
with owner
and args
. This may be a bit different from what you’re used to in Ember or other frameworks, but is normal for sub-classes in TypeScript today. If the compiler just accepted any ...arguments
, a lot of potentially very unsafe invocations would go through. So, instead of using ...arguments
, we explicitly pass the specific arguments and make sure their types match up with what the super-class expects....arguments
could become safe.owner
here and args
line up with what the constructor
for Glimmer components expect. The owner
is specified as unknown
because this is a detail we explicitly don’t need to know about. The args
are {}
because a Glimmer component always receives an object containing its arguments, even if the caller didn’t pass anything: then it would just be an empty object.{}
is an empty object type – all objects extend from it, but there will be no properties on it. This is distinct from the object
type, which the TypeScript docs describe as:any thing that is notnumber
,string
,boolean
,symbol
,null
, orundefined
.
object
, we could end up with TypeScript thinking args
were an array, or a Set
, or anything else that isn’t a primitive. Since we have {}
, we know that it's an object.args
passed to a Glimmer Component are available on this
, so we could change our definition to return the names of the arguments from a getter:args
this.args
is. In the constructor
version, we explicitly named the type of the args
argument. Here, it seems to just work automatically. This works because the type definition for a Glimmer component looks roughly like this:<Args>
at all? We highly recommend the TypeScript Deep Dive book’s chapter on generics to be quite helpful in understanding this part.Args extends {} = {}
, means that the component always has a property named args
—Args
{}
– an object= {}
Array
: since you can have an array of string
, or an array of number
or an array of SomeFancyObject
, the type of array is Array<T>
, where T
is the type of thing in the array, which TypeScript normally figures out for you automatically at compile time:args
as a string, or undefined
, or whatever: it has to be an object. Thus, Component<Args extends {}>
. But we also want to make it so that you can just write extends Component
, so that needs to have a default value. Thus, Component<Args extends {} = {}>
.args
a typetsconfig.json
settings (with strictNullChecks: true
), this wouldn't type-check if we didn't check whether the bio
argument were set.Args extends FancyInputArgs
means that subclasses can have more than these args, but not fewer. Specifying that the Args = FancyInputArgs
means that they default to just being FancyInputArgs
, so users don't need to supply an explicit generic type parameter here unless they're adding more arguments to the class.