fix(compiler): correct confusion between field and property names (#38685)
The `R3TargetBinder` accepts an interface for directive metadata which declares types for `input` and `output` objects. These types convey the mapping between the property names for an input or output and the corresponding property name on the component class. Due to `R3TargetBinder`'s requirements, this mapping was specified with property names as keys and field names as values. However, because of duck typing, this interface was accidentally satisifed by the opposite mapping, of field names to property names, that was produced in other parts of the compiler. This form more naturally represents the data model for inputs. Rather than accept the field -> property mapping and invert it, this commit introduces a new abstraction for such mappings which is bidirectional, eliminating the ambiguous plain object type. This mapping uses new, unambiguous terminology ("class property name" and "binding property name") and can be used to satisfy both the needs of the binder as well as those of the template type-checker (field -> property). A new test ensures that the input/output metadata produced by the compiler during analysis is directly compatible with the binder via this unambiguous new interface. PR Close #38685
This commit is contained in:
@ -26,6 +26,16 @@ export interface Target {
|
||||
template?: Node[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A data structure which can indicate whether a given property name is present or not.
|
||||
*
|
||||
* This is used to represent the set of inputs or outputs present on a directive, and allows the
|
||||
* binder to query for the presence of a mapping for property names.
|
||||
*/
|
||||
export interface InputOutputPropertySet {
|
||||
hasBindingPropertyName(propertyName: string): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata regarding a directive that's needed to match it against template elements. This is
|
||||
* provided by a consumer of the t2 APIs.
|
||||
@ -46,14 +56,14 @@ export interface DirectiveMeta {
|
||||
*
|
||||
* Goes from property names to field names.
|
||||
*/
|
||||
inputs: {[property: string]: string|[string, string]};
|
||||
inputs: InputOutputPropertySet;
|
||||
|
||||
/**
|
||||
* Set of outputs which this directive claims.
|
||||
*
|
||||
* Goes from property names to field names.
|
||||
*/
|
||||
outputs: {[property: string]: string};
|
||||
outputs: InputOutputPropertySet;
|
||||
|
||||
/**
|
||||
* Name under which the directive is exported, if any (exportAs in Angular).
|
||||
|
@ -278,7 +278,7 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
|
||||
type BoundNode = BoundAttribute|BoundEvent|TextAttribute;
|
||||
const setAttributeBinding =
|
||||
(attribute: BoundNode, ioType: keyof Pick<DirectiveMeta, 'inputs'|'outputs'>) => {
|
||||
const dir = directives.find(dir => dir[ioType].hasOwnProperty(attribute.name));
|
||||
const dir = directives.find(dir => dir[ioType].hasBindingPropertyName(attribute.name));
|
||||
const binding = dir !== undefined ? dir : node;
|
||||
this.bindings.set(attribute, binding);
|
||||
};
|
||||
|
Reference in New Issue
Block a user