So we get some data from an API:
interface ResultsFromTheApi {
usefulInfo: string;
moreUsefulInfo: number;
thingsWeCareAbout: boolean;
factAboutCats: string;
weatherReport: WeatherReport;
diagnosticInformation: Diagnostics;
addressOfOurHeadOffice: string;
dateTheApiWasCompiled: string;
}
We use some of the fields, but since the type is provided by the API creator, it contains
all the fields (even the ones we don’t care about like weatherReport
).
const result: ResultsFromTheApi = fancyApi.getAllTheThings();
doSomething(result.usefulInfo, result.moreUsefulInfo, result.thingsWeCareAbout);
This is a little annoying, because in our test we create a full ResultsFromTheApi
object
even though most of the fields are irrelevant.
It gets more annoying in the future, when we want to update the API to a new version that has some changes – but they are all changes that don’t affect us.
interface ResultsFromTheApi {
usefulInfo: string;
moreUsefulInfo: number;
thingsWeCareAbout: boolean;
- factAboutCats: string;
+ factAboutDogs: string;
weatherReport: WeatherReport;
- diagnosticInformation: Diagnostics;
+ diagnosticInfo: Diagnostics;
- addressOfOurHeadOffice: string;
+ addressOfOurHeadOffice: string[];
When we pull in the new version, we get some compiler errors because our test data
doesn’t match the ResultsFromTheApi
interface anymore. We don’t use factAboutCats
,
diagnosticInformation
, or addressOfOurHeadOffice
, so we don’t
care if the interface changed. It’s just noise.
We can fix the compiler errors by updating our test data to match the new API, but it’s a waste of time because the code doesn’t use any of the changed fields. The change makes no impact on our code, yet we have to make some updates so it will compile.
We could also get around this with using any
type, but if we’re using any
, what’s the
point of using TypeScript instead of regular JavaScript? We want our compiler to check
our code, but we don’t want it to complain about irrelevant changes.
Narrowing Our Types
What if we could tell the compiler that except for the three fields we need, we don’t care what the rest of that object contains?
In TypeScript, there are all kinds of nifty utility classes for transforming types.
Use Pick
to take a subset of properties
The ResultsFromTheApi
interface is provided for us by the API vendor and which includes
many superfluous fields. Instead of using it directly in our code, we can narrow the type
to only the part we want.
type ApiResultsWeNeed = Pick<ResultsFromTheApi, 'usefulInfo' | 'moreUsefulInfo' | 'thingsWeCareAbout'>;
const result: ApiResultsWeNeed = fancyApi.getAllTheThings();
doSomething(result.usefulInfo, result.moreUsefulInfo, result.thingsWeCareAbout);
The type ApiResultsWeNeed
is a subset of ResultsFromTheApi
that contains only the three
fields that are relevant to our work. That means that when ResultsFromTheApi
changes,
it only impacts our code if the parts we use change.
More TypeScript utility types
Pick is one of many utility types provided by TypeScript. You may find it helpful to skim read the list, so the next time you want to modify a type, you have some ideas of what utility types to reach for.