* Switch index signature storage to 'indexInfos: IndexInfo[]' property * Accept new baselines * Remove another usage of IndexKind enum * Update getIndexedAccessType and resolveMappedTypeMembers * Accept new baselines * Update grammar checking for index signatures * Accept new baselines * Consider all index signatures in mapped types and union types * Accept new baselines * Update getIndexType * Accept new baselines * Intersect multiple applicable index signatures * Use getApplicableIndexInfo instead of hardwired string/number handling * Update index signature relationship checking * Report type for which index signature is missing * Report type for which index signature is missing * Accept new baselines * Make 'number' index signatures consistently apply to numeric strings * Accept new baselines * Update fourslash test * Revise index constraint checking * Accept new baselines * Update error messages * Accept new baselines * Update type inference from index signatures * Update isKnownProperty * Update contextual typing based on index signatures * Accept new baselines * Support union types in index signature declarations * Accept new baselines * Check duplicate index signatures / remove redundant template literals from unions with string * Accept new baselines * Include key type in diagnostic / check symbol-named properties * Accept new baselines * Minor fix * Add tests * Accept new baselines * Add optimized findApplicableIndexInfoForName * Accept new baselines * Another place we don't need to obtain literal type for property name * Accept new baselines * Don't create literal types that are going to be discarded * Individual maps for string, number, bigint, and enum literal types * Remove ineffective optimizations * Accept new baselines * Permit intersections as key types in index signatures * Index expression in element access is template literal context * Add tests * Accept new baselines * Symbol index signatures from object literals with computed symbol properties * Accept new baselines * Add more tests * Accept new baselines * Implement Go To Definition for all applicable index signatures * Add fourslash test * Accept new API baselines
190 lines
8.1 KiB
TypeScript
190 lines
8.1 KiB
TypeScript
/** @internal */
|
|
namespace ts {
|
|
export function createGetSymbolWalker(
|
|
getRestTypeOfSignature: (sig: Signature) => Type,
|
|
getTypePredicateOfSignature: (sig: Signature) => TypePredicate | undefined,
|
|
getReturnTypeOfSignature: (sig: Signature) => Type,
|
|
getBaseTypes: (type: Type) => Type[],
|
|
resolveStructuredTypeMembers: (type: ObjectType) => ResolvedType,
|
|
getTypeOfSymbol: (sym: Symbol) => Type,
|
|
getResolvedSymbol: (node: Node) => Symbol,
|
|
getConstraintOfTypeParameter: (typeParameter: TypeParameter) => Type | undefined,
|
|
getFirstIdentifier: (node: EntityNameOrEntityNameExpression) => Identifier,
|
|
getTypeArguments: (type: TypeReference) => readonly Type[]) {
|
|
|
|
return getSymbolWalker;
|
|
|
|
function getSymbolWalker(accept: (symbol: Symbol) => boolean = () => true): SymbolWalker {
|
|
const visitedTypes: Type[] = []; // Sparse array from id to type
|
|
const visitedSymbols: Symbol[] = []; // Sparse array from id to symbol
|
|
|
|
return {
|
|
walkType: type => {
|
|
try {
|
|
visitType(type);
|
|
return { visitedTypes: getOwnValues(visitedTypes), visitedSymbols: getOwnValues(visitedSymbols) };
|
|
}
|
|
finally {
|
|
clear(visitedTypes);
|
|
clear(visitedSymbols);
|
|
}
|
|
},
|
|
walkSymbol: symbol => {
|
|
try {
|
|
visitSymbol(symbol);
|
|
return { visitedTypes: getOwnValues(visitedTypes), visitedSymbols: getOwnValues(visitedSymbols) };
|
|
}
|
|
finally {
|
|
clear(visitedTypes);
|
|
clear(visitedSymbols);
|
|
}
|
|
},
|
|
};
|
|
|
|
function visitType(type: Type | undefined): void {
|
|
if (!type) {
|
|
return;
|
|
}
|
|
|
|
if (visitedTypes[type.id]) {
|
|
return;
|
|
}
|
|
visitedTypes[type.id] = type;
|
|
|
|
// Reuse visitSymbol to visit the type's symbol,
|
|
// but be sure to bail on recuring into the type if accept declines the symbol.
|
|
const shouldBail = visitSymbol(type.symbol);
|
|
if (shouldBail) return;
|
|
|
|
// Visit the type's related types, if any
|
|
if (type.flags & TypeFlags.Object) {
|
|
const objectType = type as ObjectType;
|
|
const objectFlags = objectType.objectFlags;
|
|
if (objectFlags & ObjectFlags.Reference) {
|
|
visitTypeReference(type as TypeReference);
|
|
}
|
|
if (objectFlags & ObjectFlags.Mapped) {
|
|
visitMappedType(type as MappedType);
|
|
}
|
|
if (objectFlags & (ObjectFlags.Class | ObjectFlags.Interface)) {
|
|
visitInterfaceType(type as InterfaceType);
|
|
}
|
|
if (objectFlags & (ObjectFlags.Tuple | ObjectFlags.Anonymous)) {
|
|
visitObjectType(objectType);
|
|
}
|
|
}
|
|
if (type.flags & TypeFlags.TypeParameter) {
|
|
visitTypeParameter(type as TypeParameter);
|
|
}
|
|
if (type.flags & TypeFlags.UnionOrIntersection) {
|
|
visitUnionOrIntersectionType(type as UnionOrIntersectionType);
|
|
}
|
|
if (type.flags & TypeFlags.Index) {
|
|
visitIndexType(type as IndexType);
|
|
}
|
|
if (type.flags & TypeFlags.IndexedAccess) {
|
|
visitIndexedAccessType(type as IndexedAccessType);
|
|
}
|
|
}
|
|
|
|
function visitTypeReference(type: TypeReference): void {
|
|
visitType(type.target);
|
|
forEach(getTypeArguments(type), visitType);
|
|
}
|
|
|
|
function visitTypeParameter(type: TypeParameter): void {
|
|
visitType(getConstraintOfTypeParameter(type));
|
|
}
|
|
|
|
function visitUnionOrIntersectionType(type: UnionOrIntersectionType): void {
|
|
forEach(type.types, visitType);
|
|
}
|
|
|
|
function visitIndexType(type: IndexType): void {
|
|
visitType(type.type);
|
|
}
|
|
|
|
function visitIndexedAccessType(type: IndexedAccessType): void {
|
|
visitType(type.objectType);
|
|
visitType(type.indexType);
|
|
visitType(type.constraint);
|
|
}
|
|
|
|
function visitMappedType(type: MappedType): void {
|
|
visitType(type.typeParameter);
|
|
visitType(type.constraintType);
|
|
visitType(type.templateType);
|
|
visitType(type.modifiersType);
|
|
}
|
|
|
|
function visitSignature(signature: Signature): void {
|
|
const typePredicate = getTypePredicateOfSignature(signature);
|
|
if (typePredicate) {
|
|
visitType(typePredicate.type);
|
|
}
|
|
forEach(signature.typeParameters, visitType);
|
|
|
|
for (const parameter of signature.parameters) {
|
|
visitSymbol(parameter);
|
|
}
|
|
visitType(getRestTypeOfSignature(signature));
|
|
visitType(getReturnTypeOfSignature(signature));
|
|
}
|
|
|
|
function visitInterfaceType(interfaceT: InterfaceType): void {
|
|
visitObjectType(interfaceT);
|
|
forEach(interfaceT.typeParameters, visitType);
|
|
forEach(getBaseTypes(interfaceT), visitType);
|
|
visitType(interfaceT.thisType);
|
|
}
|
|
|
|
function visitObjectType(type: ObjectType): void {
|
|
const resolved = resolveStructuredTypeMembers(type);
|
|
for (const info of resolved.indexInfos) {
|
|
visitType(info.keyType);
|
|
visitType(info.type);
|
|
}
|
|
for (const signature of resolved.callSignatures) {
|
|
visitSignature(signature);
|
|
}
|
|
for (const signature of resolved.constructSignatures) {
|
|
visitSignature(signature);
|
|
}
|
|
for (const p of resolved.properties) {
|
|
visitSymbol(p);
|
|
}
|
|
}
|
|
|
|
function visitSymbol(symbol: Symbol | undefined): boolean {
|
|
if (!symbol) {
|
|
return false;
|
|
}
|
|
const symbolId = getSymbolId(symbol);
|
|
if (visitedSymbols[symbolId]) {
|
|
return false;
|
|
}
|
|
visitedSymbols[symbolId] = symbol;
|
|
if (!accept(symbol)) {
|
|
return true;
|
|
}
|
|
const t = getTypeOfSymbol(symbol);
|
|
visitType(t); // Should handle members on classes and such
|
|
if (symbol.exports) {
|
|
symbol.exports.forEach(visitSymbol);
|
|
}
|
|
forEach(symbol.declarations, d => {
|
|
// Type queries are too far resolved when we just visit the symbol's type
|
|
// (their type resolved directly to the member deeply referenced)
|
|
// So to get the intervening symbols, we need to check if there's a type
|
|
// query node on any of the symbol's declarations and get symbols there
|
|
if ((d as any).type && (d as any).type.kind === SyntaxKind.TypeQuery) {
|
|
const query = (d as any).type as TypeQueryNode;
|
|
const entity = getResolvedSymbol(getFirstIdentifier(query.exprName));
|
|
visitSymbol(entity);
|
|
}
|
|
});
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
} |