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;},
|
members(): SymbolTable{return _this.scope;},
|
||||||
signatures(): Signature[]{return [];},
|
signatures(): Signature[]{return [];},
|
||||||
selectSignature(types): Signature | undefined{return undefined;},
|
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,
|
indexed: () => undefined,
|
||||||
|
typeArguments: () => undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -223,4 +223,6 @@ class OverrideKindSymbol implements Symbol {
|
||||||
selectSignature(types: Symbol[]) { return this.sym.selectSignature(types); }
|
selectSignature(types: Symbol[]) { return this.sym.selectSignature(types); }
|
||||||
|
|
||||||
indexed(argument: Symbol) { return this.sym.indexed(argument); }
|
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`.
|
* If the symbol cannot be indexed, this method should return `undefined`.
|
||||||
*/
|
*/
|
||||||
indexed(argument: Symbol, key?: any): Symbol|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 {
|
getElementType(type: Symbol): Symbol|undefined {
|
||||||
if (type instanceof TypeWrapper) {
|
if (type instanceof TypeWrapper) {
|
||||||
const elementType = getTypeParameterOf(type.tsType, 'Array');
|
const tSymbol = type.tsType.symbol;
|
||||||
if (elementType) {
|
const tArgs = type.typeArguments();
|
||||||
return new TypeWrapper(elementType, type.context);
|
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 {
|
private getTsTypeOf(symbol: Symbol): ts.Type|undefined {
|
||||||
const type = this.getTypeWrapper(symbol);
|
const type = getTypeWrapper(symbol);
|
||||||
return type && type.tsType;
|
return type && type.tsType;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private getTypeWrapper(symbol: Symbol): TypeWrapper|undefined {
|
function getTypeWrapper(symbol: Symbol): TypeWrapper|undefined {
|
||||||
let type: TypeWrapper|undefined = undefined;
|
let type: TypeWrapper|undefined = undefined;
|
||||||
if (symbol instanceof TypeWrapper) {
|
if (symbol instanceof TypeWrapper) {
|
||||||
type = symbol;
|
type = symbol;
|
||||||
|
@ -218,7 +219,6 @@ class TypeScriptSymbolQuery implements SymbolQuery {
|
||||||
type = symbol.type;
|
type = symbol.type;
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function typeCallable(type: ts.Type): boolean {
|
function typeCallable(type: ts.Type): boolean {
|
||||||
|
@ -290,8 +290,8 @@ class TypeWrapper implements Symbol {
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed(argument: Symbol, value: any): Symbol|undefined {
|
indexed(argument: Symbol, value: any): Symbol|undefined {
|
||||||
const type = argument instanceof TypeWrapper ? argument : argument.type;
|
const type = getTypeWrapper(argument);
|
||||||
if (!(type instanceof TypeWrapper)) return;
|
if (!type) return;
|
||||||
|
|
||||||
const typeKind = typeKindOf(type.tsType);
|
const typeKind = typeKindOf(type.tsType);
|
||||||
switch (typeKind) {
|
switch (typeKind) {
|
||||||
|
@ -311,6 +311,13 @@ class TypeWrapper implements Symbol {
|
||||||
return sType && new TypeWrapper(sType, this.context);
|
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;
|
// 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 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); }
|
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; }
|
indexed(argument: Symbol): Symbol|undefined { return undefined; }
|
||||||
|
|
||||||
|
typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); }
|
||||||
|
|
||||||
private get tsType(): ts.Type {
|
private get tsType(): ts.Type {
|
||||||
let type = this._tsType;
|
let type = this._tsType;
|
||||||
if (!type) {
|
if (!type) {
|
||||||
|
@ -405,21 +414,21 @@ class DeclaredSymbol implements Symbol {
|
||||||
|
|
||||||
get container(): Symbol|undefined { return undefined; }
|
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 definition(): Definition { return this.declaration.definition; }
|
||||||
|
|
||||||
get documentation(): ts.SymbolDisplayPart[] { return this.declaration.type.documentation; }
|
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 {
|
selectSignature(types: Symbol[]): Signature|undefined { return this.type.selectSignature(types); }
|
||||||
return this.declaration.type.selectSignature(types);
|
|
||||||
}
|
typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); }
|
||||||
|
|
||||||
indexed(argument: Symbol): Symbol|undefined { return undefined; }
|
indexed(argument: Symbol): Symbol|undefined { return undefined; }
|
||||||
}
|
}
|
||||||
|
@ -442,17 +451,14 @@ class SignatureResultOverride implements Signature {
|
||||||
get result(): Symbol { return this.resultType; }
|
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`
|
// ∀ Typescript version >= 2.2, `SymbolTable` is implemented as an ES6 `Map`
|
||||||
const result = new Map<string, ts.Symbol>();
|
const result = new Map<string, ts.Symbol>();
|
||||||
for (const symbol of symbols) {
|
for (const symbol of symbols) {
|
||||||
result.set(symbol.name, symbol);
|
result.set(symbol.name, symbol);
|
||||||
}
|
}
|
||||||
// First, tell the compiler that `result` is of type `any`. Then, use a second type assertion
|
|
||||||
// to `ts.SymbolTable`.
|
return result as ts.SymbolTable;
|
||||||
// Otherwise, `Map<string, ts.Symbol>` and `ts.SymbolTable` will be considered as incompatible
|
|
||||||
// types by the compiler
|
|
||||||
return <ts.SymbolTable>(<any>result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toSymbols(symbolTable: ts.SymbolTable | undefined): ts.Symbol[] {
|
function toSymbols(symbolTable: ts.SymbolTable | undefined): ts.Symbol[] {
|
||||||
|
@ -602,7 +608,7 @@ class PipeSymbol implements Symbol {
|
||||||
|
|
||||||
get name(): string { return this.pipe.name; }
|
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 {
|
get definition(): Definition|undefined {
|
||||||
const symbol = this.tsType.getSymbol();
|
const symbol = this.tsType.getSymbol();
|
||||||
|
@ -625,24 +631,21 @@ class PipeSymbol implements Symbol {
|
||||||
let signature = selectSignature(this.tsType, this.context, types) !;
|
let signature = selectSignature(this.tsType, this.context, types) !;
|
||||||
if (types.length > 0) {
|
if (types.length > 0) {
|
||||||
const parameterType = types[0];
|
const parameterType = types[0];
|
||||||
if (parameterType instanceof TypeWrapper) {
|
let resultType: Symbol|undefined = undefined;
|
||||||
let resultType: ts.Type|undefined = undefined;
|
|
||||||
switch (this.name) {
|
switch (this.name) {
|
||||||
case 'async':
|
case 'async':
|
||||||
// Get symbol of 'Observable', 'Promise', or 'EventEmitter' type.
|
// Get type argument of 'Observable', 'Promise', or 'EventEmitter'.
|
||||||
const symbol = parameterType.tsType.symbol;
|
const tArgs = parameterType.typeArguments();
|
||||||
if (symbol) {
|
if (tArgs && tArgs.length === 1) {
|
||||||
resultType = getTypeParameterOf(parameterType.tsType, symbol.name);
|
resultType = tArgs[0];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'slice':
|
case 'slice':
|
||||||
resultType = parameterType.tsType;
|
resultType = parameterType;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (resultType) {
|
if (resultType) {
|
||||||
signature = new SignatureResultOverride(
|
signature = new SignatureResultOverride(signature, resultType);
|
||||||
signature, new TypeWrapper(resultType, parameterType.context));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return signature;
|
return signature;
|
||||||
|
@ -650,6 +653,8 @@ class PipeSymbol implements Symbol {
|
||||||
|
|
||||||
indexed(argument: Symbol): Symbol|undefined { return undefined; }
|
indexed(argument: Symbol): Symbol|undefined { return undefined; }
|
||||||
|
|
||||||
|
typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); }
|
||||||
|
|
||||||
private get tsType(): ts.Type {
|
private get tsType(): ts.Type {
|
||||||
let type = this._tsType;
|
let type = this._tsType;
|
||||||
if (!type) {
|
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 {
|
function typeKindOf(type: ts.Type | undefined): BuiltinType {
|
||||||
if (type) {
|
if (type) {
|
||||||
if (type.flags & ts.TypeFlags.Any) {
|
if (type.flags & ts.TypeFlags.Any) {
|
||||||
|
|
Loading…
Reference in New Issue