refactor(language-service): adds Symbol#typeArguments and does cleanup (#34571)
Adds a `typeArguments` method to the `Symbol` interface, cleaning up how type parameters of a TypeScript type are currently found. This will be necessary for providing completions for `$event` variables' properties (#34570). This commit also performs some fly-by cleanups seen while implementing the `typeArguments` methods. There is more clean up to do in the `typescript_symbols` file, but the scope of this commit didn't need to get larger. PR Close #34571
This commit is contained in:
parent
15b4173a76
commit
7325053dfa
|
@ -223,7 +223,8 @@ export class AstType implements AstVisitor {
|
|||
members(): SymbolTable{return _this.scope;},
|
||||
signatures(): Signature[]{return [];},
|
||||
selectSignature(types): Signature | undefined{return undefined;},
|
||||
indexed(argument): Symbol | undefined{return undefined;}
|
||||
indexed(argument): Symbol | undefined{return undefined;},
|
||||
typeArguments(): Symbol[] | undefined{return undefined;},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ export const createGlobalSymbolTable: (query: ng.SymbolQuery) => ng.SymbolTable
|
|||
};
|
||||
},
|
||||
indexed: () => undefined,
|
||||
typeArguments: () => undefined,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
|
|
@ -223,4 +223,6 @@ class OverrideKindSymbol implements Symbol {
|
|||
selectSignature(types: Symbol[]) { return this.sym.selectSignature(types); }
|
||||
|
||||
indexed(argument: Symbol) { return this.sym.indexed(argument); }
|
||||
|
||||
typeArguments(): Symbol[]|undefined { return this.sym.typeArguments(); }
|
||||
}
|
||||
|
|
|
@ -129,6 +129,11 @@ export interface Symbol {
|
|||
* If the symbol cannot be indexed, this method should return `undefined`.
|
||||
*/
|
||||
indexed(argument: Symbol, key?: any): Symbol|undefined;
|
||||
|
||||
/**
|
||||
* Returns the type arguments of a Symbol, if any.
|
||||
*/
|
||||
typeArguments(): Symbol[]|undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -125,10 +125,10 @@ class TypeScriptSymbolQuery implements SymbolQuery {
|
|||
|
||||
getElementType(type: Symbol): Symbol|undefined {
|
||||
if (type instanceof TypeWrapper) {
|
||||
const elementType = getTypeParameterOf(type.tsType, 'Array');
|
||||
if (elementType) {
|
||||
return new TypeWrapper(elementType, type.context);
|
||||
}
|
||||
const tSymbol = type.tsType.symbol;
|
||||
const tArgs = type.typeArguments();
|
||||
if (!tSymbol || tSymbol.name !== 'Array' || !tArgs || tArgs.length != 1) return;
|
||||
return tArgs[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,11 +206,12 @@ class TypeScriptSymbolQuery implements SymbolQuery {
|
|||
}
|
||||
|
||||
private getTsTypeOf(symbol: Symbol): ts.Type|undefined {
|
||||
const type = this.getTypeWrapper(symbol);
|
||||
const type = getTypeWrapper(symbol);
|
||||
return type && type.tsType;
|
||||
}
|
||||
}
|
||||
|
||||
private getTypeWrapper(symbol: Symbol): TypeWrapper|undefined {
|
||||
function getTypeWrapper(symbol: Symbol): TypeWrapper|undefined {
|
||||
let type: TypeWrapper|undefined = undefined;
|
||||
if (symbol instanceof TypeWrapper) {
|
||||
type = symbol;
|
||||
|
@ -219,7 +220,6 @@ class TypeScriptSymbolQuery implements SymbolQuery {
|
|||
}
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
function typeCallable(type: ts.Type): boolean {
|
||||
const signatures = type.getCallSignatures();
|
||||
|
@ -290,8 +290,8 @@ class TypeWrapper implements Symbol {
|
|||
}
|
||||
|
||||
indexed(argument: Symbol, value: any): Symbol|undefined {
|
||||
const type = argument instanceof TypeWrapper ? argument : argument.type;
|
||||
if (!(type instanceof TypeWrapper)) return;
|
||||
const type = getTypeWrapper(argument);
|
||||
if (!type) return;
|
||||
|
||||
const typeKind = typeKindOf(type.tsType);
|
||||
switch (typeKind) {
|
||||
|
@ -311,6 +311,13 @@ class TypeWrapper implements Symbol {
|
|||
return sType && new TypeWrapper(sType, this.context);
|
||||
}
|
||||
}
|
||||
|
||||
typeArguments(): Symbol[]|undefined {
|
||||
// TODO: use checker.getTypeArguments when TS 3.7 lands in the monorepo.
|
||||
const typeArguments: ReadonlyArray<ts.Type> = (this.tsType as any).typeArguments;
|
||||
if (!typeArguments) return undefined;
|
||||
return typeArguments.map(ta => new TypeWrapper(ta, this.context));
|
||||
}
|
||||
}
|
||||
|
||||
// If stringIndexType a primitive type(e.g. 'string'), the Symbol is undefined;
|
||||
|
@ -342,7 +349,7 @@ class SymbolWrapper implements Symbol {
|
|||
|
||||
get kind(): DeclarationKind { return this.callable ? 'method' : 'property'; }
|
||||
|
||||
get type(): Symbol|undefined { return new TypeWrapper(this.tsType, this.context); }
|
||||
get type(): TypeWrapper { return new TypeWrapper(this.tsType, this.context); }
|
||||
|
||||
get container(): Symbol|undefined { return getContainerOf(this.symbol, this.context); }
|
||||
|
||||
|
@ -380,6 +387,8 @@ class SymbolWrapper implements Symbol {
|
|||
|
||||
indexed(argument: Symbol): Symbol|undefined { return undefined; }
|
||||
|
||||
typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); }
|
||||
|
||||
private get tsType(): ts.Type {
|
||||
let type = this._tsType;
|
||||
if (!type) {
|
||||
|
@ -405,21 +414,21 @@ class DeclaredSymbol implements Symbol {
|
|||
|
||||
get container(): Symbol|undefined { return undefined; }
|
||||
|
||||
get type() { return this.declaration.type; }
|
||||
get type(): Symbol { return this.declaration.type; }
|
||||
|
||||
get callable(): boolean { return this.declaration.type.callable; }
|
||||
get callable(): boolean { return this.type.callable; }
|
||||
|
||||
get definition(): Definition { return this.declaration.definition; }
|
||||
|
||||
get documentation(): ts.SymbolDisplayPart[] { return this.declaration.type.documentation; }
|
||||
|
||||
members(): SymbolTable { return this.declaration.type.members(); }
|
||||
members(): SymbolTable { return this.type.members(); }
|
||||
|
||||
signatures(): Signature[] { return this.declaration.type.signatures(); }
|
||||
signatures(): Signature[] { return this.type.signatures(); }
|
||||
|
||||
selectSignature(types: Symbol[]): Signature|undefined {
|
||||
return this.declaration.type.selectSignature(types);
|
||||
}
|
||||
selectSignature(types: Symbol[]): Signature|undefined { return this.type.selectSignature(types); }
|
||||
|
||||
typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); }
|
||||
|
||||
indexed(argument: Symbol): Symbol|undefined { return undefined; }
|
||||
}
|
||||
|
@ -442,17 +451,14 @@ class SignatureResultOverride implements Signature {
|
|||
get result(): Symbol { return this.resultType; }
|
||||
}
|
||||
|
||||
export function toSymbolTableFactory(symbols: ts.Symbol[]) {
|
||||
export function toSymbolTableFactory(symbols: ts.Symbol[]): ts.SymbolTable {
|
||||
// ∀ Typescript version >= 2.2, `SymbolTable` is implemented as an ES6 `Map`
|
||||
const result = new Map<string, ts.Symbol>();
|
||||
for (const symbol of symbols) {
|
||||
result.set(symbol.name, symbol);
|
||||
}
|
||||
// First, tell the compiler that `result` is of type `any`. Then, use a second type assertion
|
||||
// to `ts.SymbolTable`.
|
||||
// Otherwise, `Map<string, ts.Symbol>` and `ts.SymbolTable` will be considered as incompatible
|
||||
// types by the compiler
|
||||
return <ts.SymbolTable>(<any>result);
|
||||
|
||||
return result as ts.SymbolTable;
|
||||
}
|
||||
|
||||
function toSymbols(symbolTable: ts.SymbolTable | undefined): ts.Symbol[] {
|
||||
|
@ -602,7 +608,7 @@ class PipeSymbol implements Symbol {
|
|||
|
||||
get name(): string { return this.pipe.name; }
|
||||
|
||||
get type(): Symbol|undefined { return new TypeWrapper(this.tsType, this.context); }
|
||||
get type(): TypeWrapper { return new TypeWrapper(this.tsType, this.context); }
|
||||
|
||||
get definition(): Definition|undefined {
|
||||
const symbol = this.tsType.getSymbol();
|
||||
|
@ -625,24 +631,21 @@ class PipeSymbol implements Symbol {
|
|||
let signature = selectSignature(this.tsType, this.context, types) !;
|
||||
if (types.length > 0) {
|
||||
const parameterType = types[0];
|
||||
if (parameterType instanceof TypeWrapper) {
|
||||
let resultType: ts.Type|undefined = undefined;
|
||||
let resultType: Symbol|undefined = undefined;
|
||||
switch (this.name) {
|
||||
case 'async':
|
||||
// Get symbol of 'Observable', 'Promise', or 'EventEmitter' type.
|
||||
const symbol = parameterType.tsType.symbol;
|
||||
if (symbol) {
|
||||
resultType = getTypeParameterOf(parameterType.tsType, symbol.name);
|
||||
// Get type argument of 'Observable', 'Promise', or 'EventEmitter'.
|
||||
const tArgs = parameterType.typeArguments();
|
||||
if (tArgs && tArgs.length === 1) {
|
||||
resultType = tArgs[0];
|
||||
}
|
||||
break;
|
||||
case 'slice':
|
||||
resultType = parameterType.tsType;
|
||||
resultType = parameterType;
|
||||
break;
|
||||
}
|
||||
if (resultType) {
|
||||
signature = new SignatureResultOverride(
|
||||
signature, new TypeWrapper(resultType, parameterType.context));
|
||||
}
|
||||
signature = new SignatureResultOverride(signature, resultType);
|
||||
}
|
||||
}
|
||||
return signature;
|
||||
|
@ -650,6 +653,8 @@ class PipeSymbol implements Symbol {
|
|||
|
||||
indexed(argument: Symbol): Symbol|undefined { return undefined; }
|
||||
|
||||
typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); }
|
||||
|
||||
private get tsType(): ts.Type {
|
||||
let type = this._tsType;
|
||||
if (!type) {
|
||||
|
@ -798,15 +803,6 @@ function getContainerOf(symbol: ts.Symbol, context: TypeContext): Symbol|undefin
|
|||
}
|
||||
}
|
||||
|
||||
function getTypeParameterOf(type: ts.Type, name: string): ts.Type|undefined {
|
||||
if (type && type.symbol && type.symbol.name == name) {
|
||||
const typeArguments: ts.Type[] = (type as any).typeArguments;
|
||||
if (typeArguments && typeArguments.length <= 1) {
|
||||
return typeArguments[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function typeKindOf(type: ts.Type | undefined): BuiltinType {
|
||||
if (type) {
|
||||
if (type.flags & ts.TypeFlags.Any) {
|
||||
|
|
Loading…
Reference in New Issue