rest.js: Cannot access `content` property on result of octokit.repos.getContent()

The Typescript types for octokit.repos.getContent seem to be broken. Previously it would return a Promise<OctokitResponse<ReposGetContentResponseData>> which had a data property with a content property.

Now i get TypeScript errors trying to get to content - either through .data or directly.

 'content' does not exist on type '{ type: string; size: number; name: string; path: string; content?: string; sha: string; url: string; git_url: string; html_url: string; download_url: string; _links: { git: string; html: string; self: string; }; }[] | { ...; } | { ...; } | { ...; }'.
      Property 'content' does not exist on type '{ type: string; size: number; name: string; path: string; content?: string; sha: string; url: string; git_url: string; html_url: string; download_url: string; _links: { git: string; html: string; self: string; }; }[]'.

_Originally posted by @AndrewCsontos in https://github.com/octokit/octokit.js/issues/1951#issuecomment-744449274_

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 6
  • Comments: 40 (33 by maintainers)

Most upvoted comments

This is still happening to me. Here is what I did:

async function foo() {
  const response = await octokit.repos.getContent(
    {
          owner: 'me',
          repo: 'mine',
          path: 'foo/bar.json',
     })
);
    const { content } = { ...response.data };
    if (content) //Base64 decode and do stuff with content
    else //handle content not found
}

then I have my content. Hope that helps

@AndrewCsontos a workaround for now, is to add as components["schemas"]["content-file"] where components is imported from @octokit/openapi-types, whenever you want to access the content property.

Ex:

import { Octokit } from '@octokit/rest';
import { paths, components } from '@octokit/openapi-types'
const octokit = new Octokit();

type GetRepoContentResponseDataFile = components["schemas"]["content-file"]
async function foo() {
  const { data } = await octokit.repos.getContent();
  if (!Array.isArray(data)) {
    const foo = data as GetRepoContentResponseDataFile

    if (typeof foo.content !== undefined) {
      console.log(foo.content)
    }
  }
}

I have just created a PR within GitHub to fix the OpenAPI spec issue.

I used this snippet of code to test.

import { Octokit } from '@octokit/rest';
const octokit = new Octokit();

async function foo() {
  const { data } = await octokit.repos.getContent();
  if (!Array.isArray(data)) { // Filter out directories

    if (data.type === 'file') { // Make sure it's a file
      data.content;
    }
  }
}

@wolfy1339 I think this should now be fixed. Are you able to give it a try?

Everything seems to work as expected now.

Great research @oscard0m!

"content-directory": {
      type: "dir";
      ...
}[];

Note that in case of a directory response, the items can be one of the four possible types: file, dir, symlink, submodule, not just dir

@oscard0m that sounds about what I’d expect - it’s the same reason I restructured our schemas to be per-action.

As you’ve found in the handbook, you need to have a discriminating property to be able to properly type narrow, which your proposal adds 😃

If you think this is the issue… Does this mean we (I can do it myself if you want) need to open an issue in Github’s OpenAPI (I’m still not familiar with the flow and pieces)? @gr2m @wolfy1339

Yes, we should open an issue there, and reference back to this one.

Let me research a little bit more and open a StackOverflow post explaining the issue. But for sure there are multiple issues opened in TypeScript repo for this.

Did you get any answers on your StackOverflow post?

Hey @wolfy1339 sorry for the delay here. After checking a bit deeper the issue and reading some StackOverflow / Github issues open and googling a bit, I think the following solution would be the right one, let me know what do you think:

What TypeScript handbook says: we need a to have a single field which uses literal types which you can use to let TypeScript narrow down the possible current type

Checking the different types for content I think the type field is the one we should be using:

https://github.com/octokit/openapi-types.ts/blob/main/generated/types.ts#L25896-L25962

so, instead of:

"content-directory": {
	type: string;
    ...
}[];

"content-file": {
	type: string;
    ...
};

"content-symlink": {
	type: string;
	...
};

"content-submodule": {
	type: string;
	...
};

we should use:

"content-directory": {
      type: "dir";
      ...
}[];

"content-file": {
    type: "file";
	...
};

"content-symlink": {
	type: "symlink";
	...
};

"content-submodule": {
	type: "submodule";
	...
};

Which is already like this checking the examples in GitHub docs https://docs.github.com/en/free-pro-team@latest/rest/reference/repos#get-repository-content but checking their OpenAPI it is not like this: https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json

If you think this is the issue… Does this mean we (I can do it myself if you want) need to open an issue in Github’s OpenAPI (I’m still not familiar with the flow and pieces)? @gr2m @wolfy1339

P.S.: Tagging @G-Rath just in case you are interested in following up or adding your thoughts on this.

Let me research a little bit more and open a StackOverflow post explaining the issue. But for sure there are multiple issues opened in TypeScript repo for this.

Did you get any answers on your StackOverflow post?

That won’t really help much. The types are only common properties, so we’d have to add that property to all the different openapi types.

This just seems like a problem that can’t really be fixed.

@dominguezcelada Do you have any ideas how to fix this issue?

Tomorrow I will take a look into it and see if I can help 😉

I don’t find an easy solution for the problem. Searching on the Internet I found some implementations of OneOf or ExclusiveUnion like this but looks super complex to me:

What about wrapping common properties in a type and extending it with type { [key:string]: any} for the moment to unblock users and we ping TypeScript team? With more time we can research on existing issues for this OneOf implementation and what’s the best approach here.

@dominguezcelada Do you have any ideas how to fix this issue?

Tomorrow I will take a look into it and see if I can help 😉

I’ve been looking around the types and there’s 2 problems here:

  1. response.data can be an Array when the path is a directory
  2. The other types for response.data don’t include the content property (when path is a git submodule or a symlink).

The first problem shouldn’t be too much of a hassle to deal with, you can easily filter it out using Array.isArray(). The second problem is where the problem really lies, since in TypeScript you cannot access properties that aren’t common to all the different types