I have a React application, and in order to make refactoring easier, I need to add an extension method on my Product type:
product.hasAbc() // boolean check that references the attributes property
product.hasDef() // boolean check
Currently my API returns a response which then gets bound to a typescript interface type.
Below is my Product Interface
, and then decoder
that decodes the API json response. My actual axios function uses the product decoder.
export interface Product {
readonly id: string;
readonly attributes: ReadOnlyArray<string>,
...
}
export const productDecoder = JsonDecoder.object<Product>(
{
id: JsonDecoder.string,
...
},
"Product"
);
async function decodeResponse<T>(decoder: JsonDecoder.Decoder<T>, responseData: any): Promise<T> {
return decoder.decodePromise(responseData);
}
export async function getProduct(): Promise<Product> {
return new Promise((resolve, reject) => {
customAxios.get("/api/products/123")
.then(response =>
decodeResponse<Product>(productDecoder, response.data).then(resolve).catch(reject)
)
.catch(error => reject(pathOr("Error...", errorMsgPath, error)));
});
}
How can I add extension methods on the attributes so I can do:
const hasAbc = () => {
this.attributes.contains("abc")
}
Reference: The project is using ts.data which is a decoding library: https://github.com/joanllenas/ts.data.json
2
Answers
You can define a new method on
Array
object prototype. E.g:Then use it:
But this method will exist on every instance of Array object. E.g:
[1,'abc'].IsTrue()
is a valid usage.You can’t define the extension method for only one interface of your choice, because javascript actually don’t have the notion of
Interface
orType
. It’s just a language feature that Typescript bring up to help your developer life easier.The recommended approach in React world would be some Utility functions, you import and use them on demand.
I’m going to recommend a slightly different approach that I think gets at the functionality you are looking for.
First, as noted by others, an interface is a Typescript language feature not a Javascript language feature. It’s used for static type checking during compile time, and that’s ir; there is no object or class that gets created in the resulting JavaScript. As such, you can’t extend it. If you wanted to be able to extend it you’d need to use a class instead of an interface.
What you can do, however, is define special functions that will evaluate a given instance of your Product interface and inform Typescript whether or not it it has additional properties. These special functions are referred to as type guards (https://www.typescriptlang.org/docs/handbook/advanced-types.html).
So, for instance, you could write a hasAbc type guard like so:
Now if you use this function in an if-block, the typescript compiler will be happy when you reference the field abc from a Product: