How to programmatically access a file's properties' types?

What I’m trying to do

Hi,

I’m faffing around with Obsidian, in dataviewJS, trying to programmatically get the types of the active file’s properties.

I can access a page’s properties and the properties’ values like so:
const file = app.vault.getAbstractFileByPath(dv.current().file.path);
const metadata = app.metadataCache.getFileCache(file);
const frontmatter = metadata.frontmatter;

But this doesn’t get me the file’s properties’ types.

Things I have tried

I’ve been exploring objects in the console via logging, and I’ve found the types of all properties in the vault hidden in: app.metadataCache.app.metadataTypeManager.types

However, although I can see “types” in the console by logging it, I cannot access “types” with JavaScript dot notation. All the info I want is right there in front of me, I just cannot figure out how to access it programmatically, lol.

Thanks for reading to this point.

Any ideas on how I can access the types of all my vault’s properties, so that I can cross reference them with a single page’s properties by name, and get the types of an active page’s properties?

I’ve also just noticed there is a function “parseFrontMatterEntry” in the Obsidian Developer API, but I am not sure what it does or if I can use it in dataviewJS. Ever hear of it, use it, or have any ideas on how to use it in dataviewJS?

Well, I figured out how to cross-reference the properties in a file with their vault-state-type. Now, the real work can begin! Posting this here jic it helps anyone in the future.

const file = app.vault.getAbstractFileByPath(dv.current().file.path); 
const metadata = app.metadataCache.getFileCache(file); 
const frontmatter = metadata.frontmatter;
const allPropertiesOfAllPropertiesInVault = Object.getOwnPropertyDescriptors(app.metadataCache.app.metadataTypeManager.types);
for (const [key1, value1] of Object.entries(frontmatter)) {
	for (const [key2, dict2] of Object.entries(allPropertiesOfAllPropertiesInVault)){			
		if (key1.toLowerCase() === key2){
			const type2 = dict2.value.type;				
			switch(type2){
				case 'aliases':
					console.log("aliases:\t" + key1 + "\t" + value1 + "\t" + type2);
					break;					 
				case 'checkbox':
					console.log("checkbox:\t" + key1 + "\t" + value1 + "\t" + type2);
					break;
				case 'date':
					console.log("date:\t" + key1 + "\t" + value1 + "\t" + type2);
					break;
				case 'datetime':
					console.log("datetime:\t" + key1 + "\t" + value1 + "\t" + type2);
					break;
				case 'number':
					console.log("number:\t" + key1 + "\t" + value1 + "\t" + type2);
					break;
				case 'tags':
					console.log("tags:\t" + key1 + "\t" + value1 + "\t" + type2);
					break;
				case 'text':
					console.log("text:\t" + key1 + "\t" + value1 + "\t" + type2);
					break;
				case 'multitext':
					console.log("multitext:\t" + key1 + "\t" + value1 + "\t" + type2);
					break;
				default:
					console.log("ERROR:\t" + key1 + "\t" + value1 + "\t" + type2);
					break;
			}
		}
	}
}

I first got interested in this answer since you duplicated so many cases with identical output with the exception of type2, so instead of all of your switch you could easily have done:

console.log(type2 + "\t" + key1 + "\t" + value1 + "\t" + type2);

And gotten the same output… :smiley: I’d also suggest better naming to help you understand where your values comes from. If you don’t look at the code, where do value1 come from in your code? If this instead had been named “fmValue” (or similar) you could immediately tell that this the value of the frontmatter key we’re currently looping on.


After these initial thoughts I set out to take another look at your code, and I ended up with switching the logic around, and also discovering a flaw (or two) in your code:

  • First of all you’re using app.*.app.*, where you usually can ditch the first part and go directly to app.metadataTypeManager
  • You’re doing a double loop, where you first loop on your frontmatter keys (which most likely is a small number), and then on all property types (which most likely is a larger number). This is can/should be avoided, since you repeat the inner loop for each of the outer loop iteration
  • However, the most important flaw I found was that the app.metadataTypeManager.types doesn’t list all types of all properties. In fact, I’m not sure what it lists. Possibly it lists only those which you’ve explicitly set in the property editor, or which has a predefined value by Obsidian. To get to see the type of all properties you need to access the .properties section of this cache
  • Bonus tip: Some times I enjoy doing a console.table( ... ) to debug larger structures. It presents its variable in a table form readily available with sorting options, and possibilities to change the width of the columns. Can be really nifty every now and then

So here is a revised script doing almost the same, but accounting for some of the properties being found in either section of app.metadataTypeManager:

```dataviewjs
const file = app.vault.getAbstractFileByPath(dv.current().file.path); 
const metadata = app.metadataCache.getFileCache(file); 
const frontmatter = metadata.frontmatter

// Bail out if no frontmatter
if (!frontmatter) {
  console.warn("No frontmatter in ", dv.current().file.path)
  exit
} 

// Should probably add some safe guards around this as well...

const allPropWithType = app.metadataTypeManager.properties
const allTypes = app.metadataTypeManager.types
console.table(allTypes)

let result = []
for (const [fmKey, fmValue] of Object.entries(frontmatter)) {
  const fmKeyLower = fmKey.toLowerCase() 
  result.push([fmKey, allPropWithType[fmKeyLower]?.type, allTypes[fmKeyLower]?.type])
}

dv.table(["Key", "PropType?", "TypesType?"], result)
console.table(result)
```

The output from both reading mode and the console display:

PS: For an interesting read of all the different variants of console.*(), see 11 Console Methods in JavaScript for Effective Debugging | Syncfusion Blogs

1 Like

Thank you, this is enormously helpful!

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.