Say I had this code that I wanted to analyze:
interface Asdf {
name: string | number
id: string
}
How would I get a Type for each property?
I have tried using Type.getProperties() in order to retrieve the types of the properties, but I am given Symbols, with which I am unsure how to get a type from.
Type.getProperties().forEach(prop => {
prop.getDeclaredType() // ??? returns a type of any
prop.getTypeAtLocation(node) // ??? I don't have a node to pass it
})
How would this be possible? I couldn't find this in the documentation (maybe I'd be willing to make a PR to the docs for if you explain this :stuck_out_tongue:)
Thanks for this awesome project :tada:
Interesting. Seems like getDeclaredTypeOfSymbol is defined this way in the compiler api, so it will always return any for properties:
function getDeclaredTypeOfSymbol(symbol: Symbol): Type {
return tryGetDeclaredTypeOfSymbol(symbol) || errorType;
}
function tryGetDeclaredTypeOfSymbol(symbol: Symbol): Type | undefined {
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
return getDeclaredTypeOfClassOrInterface(symbol);
}
if (symbol.flags & SymbolFlags.TypeAlias) {
return getDeclaredTypeOfTypeAlias(symbol);
}
if (symbol.flags & SymbolFlags.TypeParameter) {
return getDeclaredTypeOfTypeParameter(symbol);
}
if (symbol.flags & SymbolFlags.Enum) {
return getDeclaredTypeOfEnum(symbol);
}
if (symbol.flags & SymbolFlags.EnumMember) {
return getDeclaredTypeOfEnumMember(symbol);
}
if (symbol.flags & SymbolFlags.Alias) {
return getDeclaredTypeOfAlias(symbol);
}
return undefined;
}
You could do the following, though I'm not sure if it's the right way of doing things:
for (const prop of Type.getProperties())
console.log(prop.getValueDeclarationOrThrow().getType().getText());
Alternatively, stick with the interface declaration and iterate over its properties in the ast:
const interfaceDec = sourceFile.getInterfaceOrThrow("Asdf");
for (const prop of interfaceDec.getProperties())
console.log(prop.getType().getText());
Also, thanks! I'm glad it's working out for you. As for submitting a PR to update the docs... don't worry about it for the moment because it's a little specific. I'll add more docs around symbols in the future.
Ok... I think this will present another problem:
Say I want to loop over the properties of a mapped type:
interface Asdf {
name: string | number
id: string
}
type Something = Partial<Asdf>
Now I can't get the value declaration because it is not explicitly created... (getValueDeclarationOrThrow throws)
I had some luck in getting a type using this:
Type.getProperties().forEach(prop => {
console.log(prop.compilerSymbol.type)
})
(which seems to not be documented, and also returns a TS type rather than a wapped type)
Yeah, the compilerSymbol is the symbol from the compiler api so it will only have compiler api objects on it.
ts.Symbol in the compiler API doesn't have that property as part of the public api so it can't be relied on:
interface Symbol {
readonly name: string;
getFlags(): SymbolFlags;
getEscapedName(): __String;
getName(): string;
getDeclarations(): Declaration[] | undefined;
getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[];
getJsDocTags(): JSDocTagInfo[];
}
interface Symbol {
flags: SymbolFlags;
escapedName: __String;
declarations: Declaration[];
valueDeclaration: Declaration;
members?: SymbolTable;
exports?: SymbolTable;
globalExports?: SymbolTable;
}
Try this out. I've found the following code works:
const somethingTypeAlias = sourceFile.getTypeAliasOrThrow("Something");
const somethingType = somethingTypeAlias.getType();
const properties = somethingType.getProperties();
for (const prop of properties)
console.log(prop.getTypeAtLocation(somethingTypeAlias).getText());
Edit: Make sure the strictNullChecks compiler option is set to true.
Awesome, that worked, thanks!
so i know how to get the value, but how to get the key?
oh...i know, that's getName...
marvellous!
Most helpful comment
Yeah, the
compilerSymbolis the symbol from the compiler api so it will only have compiler api objects on it.ts.Symbolin the compiler API doesn't have that property as part of the public api so it can't be relied on:Try this out. I've found the following code works:
Edit: Make sure the
strictNullCheckscompiler option is set totrue.