Skip to content
Permalink
Browse files

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
  • Loading branch information
JoostK authored and AndrewKushnir committed Dec 7, 2019
1 parent 059206b commit edfaab62bca0aa37202c4dcd489e46bc890fafac
@@ -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';

0 comments on commit edfaab6

Please sign in to comment.
You can’t perform that action at this time.