TypeScript fetch wrapper class with localStorage cache
The review will be focused on readability and testability.
Naming
I read (I think it was in Clean Code) that variables should not have a postfix like Data and Information and instead we should give things a name that they represent.
PortfolioData is actually a Portfolio while your current Portfolio is more a PortfolioRepository or PortfolioCollection.
Additionally I couldn't figure out, what Basic could mean and I looked up the json-representation of the gitconnected api:

&quot;basics&quot;: {
   &quot;name&quot;: &quot;...&quot;,
   &quot;picture&quot;: &quot;https://avatars2.githubusercontent.com/u/2121481?v=4&quot;,
   &quot;label&quot;: &quot;...&quot;,
   &quot;headline&quot;: &quot;...&quot;,
   &quot;summary&quot;: &quot;...&quot;,
   &quot;website&quot;: &quot;...&quot;,
   &quot;blog&quot;: &quot;...&quot;,
   &quot;yearsOfExperience&quot;: 2,
   &quot;id&quot;: &quot;...&quot;,
   &quot;username&quot;: &quot;...&quot;,
   &quot;karma&quot;: 22,
   &quot;email&quot;: &quot;...&quot;,
   &quot;region&quot;: &quot;...&quot;,
   &quot;location&quot;: {},
   &quot;phone&quot;: &quot;...&quot;,
   &quot;followers&quot;: 63,
   &quot;following&quot;: 94,
   &quot;profiles&quot;: []
}


For me a more descriptive name would be User or Owner. A Portfolio has an Owner and a PortfolioRepository stores Portfolio-Objects:
export interface Portfolio {
  owner: Owner;
  projects?: Project[];
}

interface Cache {
  portfolio: Portfolio;
  expiration: number;
}

class PortfolioRepository { /* ... */ }

Concerns
The class Portfolio handles multiple concerns. There are two principles which can be named to emphasize the importance for separation:

Separation of Concerns (SoC)
Single Responsibility Principle (SRP)

The concerns are

fetching (await fetch(this.url))
caching (this.setCache(...))
querying (async getPortfolio(): Promise&lt;PortfolioData&gt;)

When we separate the concerns we could accomplish something like
interface PortfolioRepository {
    get(): Promise&lt;Portfolio&gt;;
}

class ApiPortfolioRepository implements PortfolioRepository {
    constructor(private cache: Cache, 
                private api: API) {}

    async get(): Promise&lt;Portfolio&gt; {
        if (this.cache.containsPortfolio()) {
            return this.cache.get();
        }

        const portfolio = await this.api.fetch();
        this.cache.update(portfolio);
  
        return portfolio;
    }
}

This has multiple advantages. Beside the descriptive and shorter method body we can accomplish through the dependency injection via the constructor a better way to test the class.
Now the PortfolioRepository does not need to know about how caching works like the cacheDurationInMilliseconds and it does not need to know the http endpoint any more.
If we want to test the class we could simply mock the api (for exampe with mocha and chai):
import { assert } from &quot;chai&quot;;

suite('API Portfolio Repository', () =&gt; {

    const cache = ...;
    const fakeAPI = new FakeAPI();

    test('when API returns no portfolio -&gt; return undefined', () =&gt; {
        const repository = new ApiPortfolioRepository(cache, fakeAPI.withoutResponse);
        
        const portfolio = repository.get();

        assert.isUndefined(portfolio);
    });

    test('when API returns a portfolio -&gt; return portfolio', () =&gt; {
        const repository = new ApiPortfolioRepository(cache, fakeAPI.withResponse);
        
        const portfolio = repository.get();

        assert.deepEqual(portfolio, fakeAPI.portfolio);
    });

})