In the process of developing a tool, I am faced with the task of resolving generic types for the final output. It's worth mentioning that I am making use of the ts-morph
library.
To illustrate, let's consider the following source code where the ClassDeclaration
of classA
serves as the "entry-point" for type resolution:
type X<T> = { fieldInType: T };
interface Y<T> {
fieldInInterface: T;
}
class classB<T> {
fieldInB?: T;
myFunction(a: T): void { //do something here }
}
class classA extends ClassB<string> {
interfaceField?: Y<string>;
typeField?: X<string>;
}
The desired output should look like this:
types of classA:
- interface Y { fieldInInterface: string } (from interfaceField)
- type X = { fieldInType: string } (from typeField)
- string (from fieldInB in parentClass)
- myFunction(a: string): void (from myFunction in parentClass)
I am currently uncertain about the most effective way to proceed with this. At the moment, my approach involves recursively traversing all types starting from the base class (using class.getBaseClass()
for recursive traversal of base classes). I believe this method is the correct and simplest approach.
Regarding generic types, my current strategy includes:
- Awaiting encounter with a generic type during traversal (e.g., on a class or a type-alias within the class) by verifying if it has type-arguments.
- If so, accessing the relevant type parameters by referencing the type definition (e.g., jumping to the base class using
getBaseClass()
, or the interface/alias-type usinggetSymbol()
orgetAliasSymbol()
). - Iterating through all type parameters and fetching the corresponding type-argument.
- Examining whether the type-argument itself has an entry in a map (which will be explained further ahead). If it does, substituting the type argument with the resolved type. (This step is crucial for recursively defined generics.)
- Maintaining a mapping for each pair, i.e., the symbol of the i-th type-parameter to the type in the type argument.
I apologize for not providing a complete code sample at this time since I have yet to finalize its implementation.
My queries are as follows:
- Is my current method appropriate? Or could there be a specific TypeScript compiler/ts-morph function that I may have overlooked which accomplishes the required resolution?
- I came across the
method, but I am unsure of its exact usage and whether it can aid in my particular scenario.typeChecker.getTypeOfSymbolAtLocation(..)