Issue:
I am currently working with Firebase cloud functions and encountering a specific problem.
Let's consider the following function:
function giveMeAnInteger(): number {
return 123;
}
When calling this function like so:
function call() {
const result = giveMeAnInteger();
console.log(result.split(" "));
}
Typescript is able to statically check the return type and flag an error stating "Property 'split' does not exist on type 'number'".
Now, if we have a similar function published to Firebase's cloud functions:
export const giveMeAnInteger = functions.https.onCall((): number => {
return 123;
});
Using Typescript to infer the response type when calling the cloud function can lead to potential runtime errors:
const giveMeAnInteger = httpsCallable(functions, "giveMeAnInteger");
export function test() {
giveMeAnInteger().then((result) => {
const res = result.data
console.log(res.split(" "));
});
}
The error message received is "'res' is of type 'unknown'." due to the signature of httpsCallable: HttpsCallable<unknown, unknown>
Solution Attempt 1:
To address this issue, one option is to declare the return type explicitly before making the function call:
const giveMeAnInteger = httpsCallable<void, number>(functions, "giveMeAnInteger");
export function test() {
giveMeAnInteger().then((result) => {
const res = result.data
console.log(res.split(" "));
});
}
Although this resolves the typing error, it shifts the burden onto the client to set the correct return type and understand the implementation details of the cloud function.
Solution Attempt 2:
An alternative approach involves hardcoding the return type of the cloud function and exporting it:
export const giveMeAnInteger = functions.https.onCall((): number => {
return 123;
});
export type ReturnTypeOfGiveMeAnInteger = number;
By importing and utilizing the defined return type, static type checking remains accurate without exposing implementation details:
import { ReturnTypeOfGiveMeAnInteger } from ...
export function test() {
giveMeAnInteger().then((result) => {
const res = result.data as ReturnTypeOfGiveMeAnInteger
console.log(res.split(" "));
});
}
This method offers a cleaner solution but may still be considered somewhat hacky or hardcoded.
If you have any alternative solutions or additional questions, I would appreciate your input for further discussion.