A) If the value of the link is text then you can use range.getRichTextValue().getLinkUrl() (or the batched version getRichTextValues()). But beware that getRichTextValue() returns "null if the cell value is not text".
B) Or if the formula is static you may be able to use range.getFormula() and use text manipulation to extract the URL from there.
C) Otherwise (e.g. if the value is a number/date/duration), it seems the only way is to enable the Advanced Sheets service*.
Then you'll be able to use the following:
/** Returns (string | null)[][] of hyperlinks for this range. */
function getHyperlinks(range) {
// Use the Advanced Sheets service to fetch the hyperlinks.
const result = Sheets.Spreadsheets.get(
range.getSheet().getParent().getId(),
{
ranges: range.getSheet().getName() + "!" + range.getA1Notation(),
fields: "sheets.data.rowData.values.hyperlink",
},
);
// Convert `result` to a 2D array the same size as `range`.
const rowIndices = [...Array(range.getHeight()).keys()];
const colIndices = [...Array(range.getWidth()).keys()];
return rowIndices.map((r) =>
colIndices.map((c) =>
// r and c are 0-indexed row and col index relative to range.
result.sheets[0].data[0].rowData?.[r]?.values?.[c]?.hyperlink ?? null,
),
);
}
*If you're running this code from a simple trigger, you'll have to upgrade to an installable trigger to use the Advanced Sheets service.