Is there any way to, given a CallExpression with inferred type arguments, find what those type arguments are?
Example code:
class SomeClass {
public someMethod<T>(arg: T): void { }
}
// What is the inferred type of T in this call?
someClass.someMethod(7);
It's easy enough to find type arguments that were explicitly assigned in the code, but I can't figure out how to find what was inferred.
function inferTypeArguments(node: ts.CallExpression, typeChecker: ts.TypeChecker) {
node.typeArguments; // is empty
const signature = typeChecker.getResolvedSignature(node);
signature['typeArguments']; // is also empty
// This returns "<number>(arg: number): void"
// so I know that the typeChecker has the right information,
// but I would really like a ts.Type[]
typeChecker.signatureToString(signature, node, ts.TypeFormatFlags.WriteTypeArgumentsOfSignature)
}
I'd been using Simon's code for a while in my transpiler... then 3.9 came along and broke it. I've done a preliminary attempt to get it working again. Unfortunately, the mapper is an "internal" concern for typescript, so this will likely change again in the future
/* @internal - from typescript 3.9 codebase*/
const enum TypeMapKind {
Simple,
Array,
Function,
Composite,
Merged,
}
/* @internal - from typescript 3.9 codebase*/
type TypeMapper =
| { kind: TypeMapKind.Simple, source: ts.Type, target: ts.Type }
| { kind: TypeMapKind.Array, sources: readonly ts.Type[], targets: readonly ts.Type[] | undefined }
| { kind: TypeMapKind.Function, func: (t: ts.Type) => ts.Type }
| { kind: TypeMapKind.Composite | TypeMapKind.Merged, mapper1: TypeMapper, mapper2: TypeMapper };
/* basic application of the mapper - recursive for composite.*/
function typeMapper(mapper:TypeMapper, source: ts.Type): ts.Type {
switch(mapper.kind){
case TypeMapKind.Simple:
return mapper.target;
case TypeMapKind.Array:
throw Error("not implemented");
case TypeMapKind.Function:
return mapper.func(source);
case TypeMapKind.Composite:
case TypeMapKind.Merged:
return typeMapper(mapper.mapper2, source);
}
}
function inferTypeArguments(node: ts.CallExpression, typeChecker: ts.TypeChecker): ts.Type[] {
const signature:ts.Signature = typeChecker.getResolvedSignature(node);
const targetParams: ts.TypeParameter[] = signature['target'] && signature['target'].typeParameters;
if (!targetParams) {
return [];
}
if(signature['mapper'] == undefined)
return targetParams;
//typescript <= 3.8
if(typeof signature['mapper'] == "function")
return targetParams.map(p=>signature['mapper'](p));
//typescript >= 3.9....
return targetParams.map(p=> typeMapper(signature['mapper'] as TypeMapper, p));
}