fix(compiler-cli): type-check inputs that include undefined when there's coercion members (#38273)
For attribute bindings that target a directive's input, the template type checker is able to verify that the type of the input expression is compatible with the directive's declaration for said input. This checking adheres to the `strictNullChecks` flag as configured in the TypeScript compilation, such that errors are reported for expressions that include `undefined` or `null` in their type if the input's declaration does not include those types. There was a bug with this level of type-checking for directives that also declare coercion members, where binding an expression that includes the `undefined` type to a directive's input that does not include the `undefined` type would not be reported as error. This commit fixes the bug by changing the type-constructor in type-check code to use an intersection type of regular inputs and coerced inputs, instead of a union type. The union type would inadvertently allow `undefined` types to be assigned into the regular inputs, as that would still satisfy the characteristics of a union type. As a result of this change, you may start to see build failures if `strictTemplates` is enabled and `strictInputTypes` is not disabled. These errors are legitimate and some action is required to achieve a successful build: 1. Update the templates for which an error is reported and introduce the non-null assertion operator at the end of the expression. This removes the `undefined` type from the expression's type, making it appear as a valid assignment. 2. Disable `strictNullInputTypes` in the compiler options. This will implicitly add the non-null assertion operators similar to option 1, but all templates in the compilation are affected. 3. Update the directive's input declaration to include the `undefined` type, if the directive is not implemented in an external library. PR Close #38273
This commit is contained in:
parent
570d156ce4
commit
7525f3afc1
|
@ -166,8 +166,8 @@ function constructTypeCtorParameter(
|
|||
if (coercedKeys.length > 0) {
|
||||
const coercedLiteral = ts.createTypeLiteralNode(coercedKeys);
|
||||
|
||||
initType =
|
||||
initType !== null ? ts.createUnionTypeNode([initType, coercedLiteral]) : coercedLiteral;
|
||||
initType = initType !== null ? ts.createIntersectionTypeNode([initType, coercedLiteral]) :
|
||||
coercedLiteral;
|
||||
}
|
||||
|
||||
if (initType === null) {
|
||||
|
|
|
@ -179,7 +179,7 @@ TestClass.ngTypeCtor({value: 'test'});
|
|||
const typeCtor = TestClassWithCtor.members.find(isTypeCtor)!;
|
||||
const ctorText = typeCtor.getText().replace(/[ \r\n]+/g, ' ');
|
||||
expect(ctorText).toContain(
|
||||
'init: Pick<TestClass, "foo"> | { bar: typeof TestClass.ngAcceptInputType_bar; }');
|
||||
'init: Pick<TestClass, "foo"> & { bar: typeof TestClass.ngAcceptInputType_bar; }');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1498,6 +1498,39 @@ export declare class AnimationEvent {
|
|||
expect(diags[0].messageText)
|
||||
.toBe(`Type 'boolean' is not assignable to type 'string | number'.`);
|
||||
});
|
||||
|
||||
it('should give an error for undefined bindings into regular inputs when coercion members are present',
|
||||
() => {
|
||||
env.tsconfig({strictTemplates: true});
|
||||
env.write('test.ts', `
|
||||
import {Component, Directive, NgModule, Input} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'blah',
|
||||
template: '<input dir [regular]="undefined" [coerced]="1">',
|
||||
})
|
||||
export class FooCmp {
|
||||
invalidType = true;
|
||||
}
|
||||
|
||||
@Directive({selector: '[dir]'})
|
||||
export class CoercionDir {
|
||||
@Input() regular: string;
|
||||
@Input() coerced: boolean;
|
||||
|
||||
static ngAcceptInputType_coerced: boolean|number;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [FooCmp, CoercionDir],
|
||||
})
|
||||
export class FooModule {}
|
||||
`);
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(1);
|
||||
expect(diags[0].messageText)
|
||||
.toBe(`Type 'undefined' is not assignable to type 'string'.`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('legacy schema checking with the DOM schema', () => {
|
||||
|
|
Loading…
Reference in New Issue