fix(ivy): inherit static coercion members from base classes (#34296)
For Ivy's template type checker it is possible to let a directive specify static members to allow a wider type for some input: ```typescript export class MatSelect { @Input() disabled: boolean; static ngAcceptInputType_disabled: boolean | string; } ``` This allows a binding to the `MatSelect.disabled` input to be of type boolean or string, whereas the `disabled` property itself is only of type boolean. Up until now, any static `ngAcceptInputType_*` property was not inherited for subclasses of a directive class. This is cumbersome, as the directive's inputs are inherited, so any acceptance member should as well. To resolve this limitation, this commit extends the flattening of directive metadata to include the acceptance members. Fixes #33830 Resolves FW-1759 PR Close #34296
This commit is contained in:
parent
c9df9afddd
commit
22ad701134
|
@ -27,6 +27,7 @@ export function flattenInheritedDirectiveMetadata(
|
|||
|
||||
let inputs: {[key: string]: string | [string, string]} = {};
|
||||
let outputs: {[key: string]: string} = {};
|
||||
let coercedInputFields = new Set<string>();
|
||||
let isDynamic = false;
|
||||
|
||||
const addMetadata = (meta: DirectiveMeta): void => {
|
||||
|
@ -43,6 +44,10 @@ export function flattenInheritedDirectiveMetadata(
|
|||
}
|
||||
inputs = {...inputs, ...meta.inputs};
|
||||
outputs = {...outputs, ...meta.outputs};
|
||||
|
||||
for (const coercedInputField of meta.coercedInputFields) {
|
||||
coercedInputFields.add(coercedInputField);
|
||||
}
|
||||
};
|
||||
|
||||
addMetadata(topMeta);
|
||||
|
@ -51,6 +56,7 @@ export function flattenInheritedDirectiveMetadata(
|
|||
...topMeta,
|
||||
inputs,
|
||||
outputs,
|
||||
coercedInputFields,
|
||||
baseClass: isDynamic ? 'dynamic' : null,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1123,9 +1123,7 @@ export declare class AnimationEvent {
|
|||
|
||||
describe('input coercion', () => {
|
||||
beforeEach(() => {
|
||||
env.tsconfig({
|
||||
'fullTemplateTypeCheck': true,
|
||||
});
|
||||
env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});
|
||||
env.write('node_modules/@angular/material/index.d.ts', `
|
||||
import * as i0 from '@angular/core';
|
||||
|
||||
|
@ -1164,10 +1162,42 @@ export declare class AnimationEvent {
|
|||
expect(diags.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should apply coercion members of base classes', () => {
|
||||
env.write('test.ts', `
|
||||
import {Component, Directive, Input, NgModule} from '@angular/core';
|
||||
|
||||
@Directive()
|
||||
export class BaseDir {
|
||||
@Input()
|
||||
value: string;
|
||||
|
||||
static ngAcceptInputType_value: string|number;
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: '[dir]',
|
||||
})
|
||||
export class MyDir extends BaseDir {}
|
||||
|
||||
@Component({
|
||||
selector: 'blah',
|
||||
template: '<input dir [value]="someNumber">',
|
||||
})
|
||||
export class FooCmp {
|
||||
someNumber = 3;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [MyDir, FooCmp],
|
||||
})
|
||||
export class FooModule {}
|
||||
`);
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should give an error if the binding expression type is not accepted by the coercion function',
|
||||
() => {
|
||||
env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});
|
||||
|
||||
env.write('test.ts', `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
import {MatInputModule} from '@angular/material';
|
||||
|
|
Loading…
Reference in New Issue