fix(language-service): correctly determine base members of types (#15600)
Fixes #15460
This commit is contained in:
parent
19cb503531
commit
a9321b1387
|
@ -803,6 +803,7 @@ class TypeWrapper implements Symbol {
|
||||||
|
|
||||||
class SymbolWrapper implements Symbol {
|
class SymbolWrapper implements Symbol {
|
||||||
private _tsType: ts.Type;
|
private _tsType: ts.Type;
|
||||||
|
private _members: SymbolTable;
|
||||||
|
|
||||||
constructor(private symbol: ts.Symbol, private context: TypeContext) {}
|
constructor(private symbol: ts.Symbol, private context: TypeContext) {}
|
||||||
|
|
||||||
|
@ -825,7 +826,18 @@ class SymbolWrapper implements Symbol {
|
||||||
|
|
||||||
get definition(): Definition { return definitionFromTsSymbol(this.symbol); }
|
get definition(): Definition { return definitionFromTsSymbol(this.symbol); }
|
||||||
|
|
||||||
members(): SymbolTable { return new SymbolTableWrapper(this.symbol.members, this.context); }
|
members(): SymbolTable {
|
||||||
|
if (!this._members) {
|
||||||
|
if ((this.symbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface)) != 0) {
|
||||||
|
const declaredType = this.context.checker.getDeclaredTypeOfSymbol(this.symbol);
|
||||||
|
const typeWrapper = new TypeWrapper(declaredType, this.context);
|
||||||
|
this._members = typeWrapper.members();
|
||||||
|
} else {
|
||||||
|
this._members = new SymbolTableWrapper(this.symbol.members, this.context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._members;
|
||||||
|
}
|
||||||
|
|
||||||
signatures(): Signature[] { return signaturesOf(this.tsType, this.context); }
|
signatures(): Signature[] { return signaturesOf(this.tsType, this.context); }
|
||||||
|
|
||||||
|
@ -1044,7 +1056,7 @@ class PipeSymbol implements Symbol {
|
||||||
}
|
}
|
||||||
|
|
||||||
private findTransformMethodType(classSymbol: ts.Symbol): ts.Type {
|
private findTransformMethodType(classSymbol: ts.Symbol): ts.Type {
|
||||||
const transform = classSymbol.members['transform'];
|
const transform = classSymbol.members && classSymbol.members['transform'];
|
||||||
if (transform) {
|
if (transform) {
|
||||||
return this.context.checker.getTypeOfSymbolAtLocation(transform, this.context.node);
|
return this.context.checker.getTypeOfSymbolAtLocation(transform, this.context.node);
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,6 +171,36 @@ describe('diagnostics', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Issue #15460
|
||||||
|
it('should be able to find members defined on an ancestor type', () => {
|
||||||
|
const app_component = `
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { NgForm } from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'example-app',
|
||||||
|
template: \`
|
||||||
|
<form #f="ngForm" (ngSubmit)="onSubmit(f)" novalidate>
|
||||||
|
<input name="first" ngModel required #first="ngModel">
|
||||||
|
<input name="last" ngModel>
|
||||||
|
<button>Submit</button>
|
||||||
|
</form>
|
||||||
|
<p>First name value: {{ first.value }}</p>
|
||||||
|
<p>First name valid: {{ first.valid }}</p>
|
||||||
|
<p>Form value: {{ f.value | json }}</p>
|
||||||
|
<p>Form valid: {{ f.valid }}</p>
|
||||||
|
\`,
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
onSubmit(form: NgForm) {}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const fileName = '/app/app.component.ts';
|
||||||
|
mockHost.override(fileName, app_component);
|
||||||
|
const diagnostic = ngService.getDiagnostics(fileName);
|
||||||
|
expect(diagnostic).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
function addCode(code: string, cb: (fileName: string, content?: string) => void) {
|
function addCode(code: string, cb: (fileName: string, content?: string) => void) {
|
||||||
const fileName = '/app/app.component.ts';
|
const fileName = '/app/app.component.ts';
|
||||||
const originalContent = mockHost.getFileContent(fileName);
|
const originalContent = mockHost.getFileContent(fileName);
|
||||||
|
|
Loading…
Reference in New Issue