fix(language-service): treat string unions as strings (#13406)
Fixes #13403
This commit is contained in:
parent
e23076f767
commit
6f330a5fc9
|
@ -571,25 +571,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
|
||||||
private program: ts.Program, private checker: ts.TypeChecker, private source: ts.SourceFile,
|
private program: ts.Program, private checker: ts.TypeChecker, private source: ts.SourceFile,
|
||||||
private fetchPipes: () => SymbolTable) {}
|
private fetchPipes: () => SymbolTable) {}
|
||||||
|
|
||||||
getTypeKind(symbol: Symbol): BuiltinType {
|
getTypeKind(symbol: Symbol): BuiltinType { return typeKindOf(this.getTsTypeOf(symbol)); }
|
||||||
const type = this.getTsTypeOf(symbol);
|
|
||||||
if (type) {
|
|
||||||
if (type.flags & ts.TypeFlags.Any) {
|
|
||||||
return BuiltinType.Any;
|
|
||||||
} else if (
|
|
||||||
type.flags &
|
|
||||||
(ts.TypeFlags.String | ts.TypeFlags.StringLike | ts.TypeFlags.StringLiteral)) {
|
|
||||||
return BuiltinType.String;
|
|
||||||
} else if (type.flags & (ts.TypeFlags.Number | ts.TypeFlags.NumberLike)) {
|
|
||||||
return BuiltinType.Number;
|
|
||||||
} else if (type.flags & (ts.TypeFlags.Undefined)) {
|
|
||||||
return BuiltinType.Undefined;
|
|
||||||
} else if (type.flags & (ts.TypeFlags.Null)) {
|
|
||||||
return BuiltinType.Null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return BuiltinType.Other;
|
|
||||||
}
|
|
||||||
|
|
||||||
getBuiltinType(kind: BuiltinType): Symbol {
|
getBuiltinType(kind: BuiltinType): Symbol {
|
||||||
// TODO: Replace with typeChecker API when available.
|
// TODO: Replace with typeChecker API when available.
|
||||||
|
@ -1223,4 +1205,35 @@ function getTypeParameterOf(type: ts.Type, name: string): ts.Type {
|
||||||
return typeArguments[0];
|
return typeArguments[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function typeKindOf(type: ts.Type): BuiltinType {
|
||||||
|
if (type) {
|
||||||
|
if (type.flags & ts.TypeFlags.Any) {
|
||||||
|
return BuiltinType.Any;
|
||||||
|
} else if (
|
||||||
|
type.flags & (ts.TypeFlags.String | ts.TypeFlags.StringLike | ts.TypeFlags.StringLiteral)) {
|
||||||
|
return BuiltinType.String;
|
||||||
|
} else if (type.flags & (ts.TypeFlags.Number | ts.TypeFlags.NumberLike)) {
|
||||||
|
return BuiltinType.Number;
|
||||||
|
} else if (type.flags & (ts.TypeFlags.Undefined)) {
|
||||||
|
return BuiltinType.Undefined;
|
||||||
|
} else if (type.flags & (ts.TypeFlags.Null)) {
|
||||||
|
return BuiltinType.Null;
|
||||||
|
} else if (type.flags & ts.TypeFlags.Union) {
|
||||||
|
// If all the constituent types of a union are the same kind, it is also that kind.
|
||||||
|
let candidate: BuiltinType;
|
||||||
|
const unionType = type as ts.UnionType;
|
||||||
|
if (unionType.types.length > 0) {
|
||||||
|
candidate = typeKindOf(unionType.types[0]);
|
||||||
|
for (const subType of unionType.types) {
|
||||||
|
if (candidate != typeKindOf(subType)) {
|
||||||
|
return BuiltinType.Other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BuiltinType.Other;
|
||||||
}
|
}
|
|
@ -121,6 +121,15 @@ describe('diagnostics', () => {
|
||||||
code, fileName => { expect(() => ngService.getDiagnostics(fileName)).not.toThrow(); });
|
code, fileName => { expect(() => ngService.getDiagnostics(fileName)).not.toThrow(); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not report an error for sub-types of string', () => {
|
||||||
|
const code =
|
||||||
|
` @Component({template: \`<div *ngIf="something === 'foo'"></div>\`}) export class MyComponent { something: 'foo' | 'bar'; }`;
|
||||||
|
addCode(code, fileName => {
|
||||||
|
const diagnostics = ngService.getDiagnostics(fileName);
|
||||||
|
onlyModuleDiagnostics(diagnostics);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
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);
|
||||||
|
@ -137,6 +146,13 @@ describe('diagnostics', () => {
|
||||||
function onlyModuleDiagnostics(diagnostics: Diagnostics) {
|
function onlyModuleDiagnostics(diagnostics: Diagnostics) {
|
||||||
// Expect only the 'MyComponent' diagnostic
|
// Expect only the 'MyComponent' diagnostic
|
||||||
expect(diagnostics.length).toBe(1);
|
expect(diagnostics.length).toBe(1);
|
||||||
|
if (diagnostics.length > 1) {
|
||||||
|
for (const diagnostic of diagnostics) {
|
||||||
|
if (diagnostic.message.indexOf('MyComponent') >= 0) continue;
|
||||||
|
console.error(`(${diagnostic.span.start}:${diagnostic.span.end}): ${diagnostic.message}`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
expect(diagnostics[0].message.indexOf('MyComponent') >= 0).toBeTruthy();
|
expect(diagnostics[0].message.indexOf('MyComponent') >= 0).toBeTruthy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue