Support Signature types for modifiers#210
Merged
chriskrycho merged 1 commit intomasterfrom Mar 21, 2022
Merged
Conversation
Details ------- Following the design pioneered in emberjs/rfcs#748, introduce a new `Signature` type and use it in defining the types for both function- and class-based modifiers. This allows end users to write, for example: interface PlaySig { Args: { Named: { when: boolean; } } Element: HTMLMediaElement; } export default modifier<PlaySig>((el, _, { when: shouldPlay }) => { if (shouldPlay) { el.play(); } else { el.pause(); } }); This supports both `Named` and `Positional` args, and works equally well with class-based modifiers. It also provides the `Element` type as `Element` by default, and provides internal type utilities as well as one external-facing type utility to make writing the type of `args` when passed to a modifier `constructor` easy to do in terms of the `Signature` which represents the modifier. Users do not *need* to type this form: the form using basic inference continues to work as well: export default modifier(( el: HTMLMediaElement, _: [], { when: shouldPlay }: { when: boolean } ) => { if (shouldPlay) { el.play(); } else { el.pause(); } })); That is: the support for `Signatures` is strictly *new* capability which was not present before. The same kind of signature works with class-based modifiers as well, while retaining backward compatibility with the `Args`-based signature and declaration on the subclass `element` field directly. To support that in a way that does not require users to name their args over and over again, introduce an `ArgsFor` type utility which translates from the signature type to the runtime form: import Modifier, { ArgsFor } from 'ember-modifier'; interface PlaySig { Args: { Named: { when: boolean; }; }; Element: HTMLMediaElement; } class MyModifier extends Modifier<PlaySig> { constructor(owner: unknown, args: ArgsFor<PlaySig>) { super(owner, args); // ... } didReceiveArguments() { const shouldPlay = this.args.named.when; if (shouldPlay) { this.element.play(); } else { this.element.pause(); } } } Additionally, the `modifier` function now returns an opaque `FunctionBasedModifier` type. This mostly exists to provide nice hooks for tooling to hook onto, but it has the added benefit of showing something besides `unknown` in an editor, and that "something" shows the arguments and element specified for the modifier. Supporting changes ------------------ - Update the README to match, and fix a *lot* of long-since outdated documentation along the way. - Loosen type test constraint to support older TS versions The new type signature *is* compatible (as the tests on TS 4.5+ show), but on older versions of TS, it does not check under a type equality check, only under a type matching check. For the purposes of the specific test in question, that's perfectly fine, because the *other* type tests cover inference and resolution correctly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Details
Following the design pioneered in emberjs/rfcs#748, introduce a new
Signaturetype and use it in defining the types for both function- and class-based modifiers. This allows end users to write, for example:This supports both
NamedandPositionalargs, and works equally well with class-based modifiers. It also provides theElementtype asElementby default, and provides internal type utilities as well as one external-facing type utility to make writing the type ofargswhen passed to a modifierconstructoreasy to do in terms of theSignaturewhich represents the modifier.Users do not need to type this form: the form using basic inference continues to work as well:
That is: the support for
Signaturesis strictly new capability which was not present before.The same kind of signature works with class-based modifiers as well, while retaining backward compatibility with the
Args-based signature and declaration on the subclasselementfield directly. To support that in a way that does not require users to name their args over and over again, introduce anArgsFortype utility which translates from the signature type to the runtime form:Additionally, the
modifierfunction now returns an opaqueFunctionBasedModifiertype. This mostly exists to provide nice hooks for tooling to hook onto, but it has the added benefit of showing something besidesunknownin an editor, and that "something" shows the arguments and element specified for the modifier.Supporting changes
Update the README to match, and fix a lot of long-since outdated documentation along the way.
Loosen type test constraint to support older TS versions
The new type signature is compatible (as the tests on TS 4.5+ show), but on older versions of TS, it does not check under a type equality check, only under a type matching check. For the purposes of the specific test in question, that's perfectly fine, because the other type tests cover inference and resolution correctly.