String as base-65535 number
Semi-serious answer suggesting true conversion (or "encoding"), as opposed to "hashing".
Examples of <string> = <its number>:
Foo = 300645810246
Bar = 283465547859
Baz = 283465547867
Gazonk = 85828974057640899041361932
Qux = 347889401940
💩️ = 237752950978979
💩︎ = 237752950978978
Code (sandbox)
function base0xFFFFtoBigInt(str) {
const base = BigInt(0xFFFF);
let result = BigInt(0);
let pow = 0;
let i = str.length;
while (i-- > 0) {
const charCode = BigInt(str.charCodeAt(i));
result += charCode * (base ** BigInt(pow++));
}
return result
}
function bigIntToBase0xFFFF(int) {
int = BigInt(int);
const base = BigInt(0xFFFF);
let result = '';
while (int) {
let rest = int % base;
result = String.fromCharCode(Number(rest)) + result;
int -= rest;
int /= base;
}
return result;
}
document.forms[0].elements[0].oninput();
document.forms[0].onsubmit()
<form action=""
onsubmit="out.value = `${str.value} = ${num.value}
${out.value}`;return false">
<input name=str type="string" value="Foo"
oninput="num.value=base0xFFFFtoBigInt(value)" >
<!--
`type="numer"` input fails to operate with really large values, miserably
-->
<input name=num type="text"
oninput="str.value=bigIntToBase0xFFFF(value)" >
<button>Save</button>
<br>
<output name=out
style="white-space:pre-wrap;word-break:break-word">
</output>
</form>
Trivia
Strings in JS are technically sequences of 16-bit "char codes" of values from 0 to 65535 (0xFFFF), so we can interpret any string as a "base-65535" number and convert it to decimal representation. (For strings longer than three characters, BigDecimal is necessary.)
Rationale
Since the question does not specify what kind of number is the target representation, only that it has to be "100% unique" (not apparent whether globally or locally), and what size of string will be processed, let's assume that the target number representation is a sequence of decimal digits of unlimited length, and let's remind that there is no way to shrink larger input into smaller hash without introducing risk of collision, i.e. breaking the (global) 100% unique rule. (It is true that significantly large hashes tend to have significantly smaller chance of collision, but technically for arbitrary large globally unique input cannot exist output of fixed size that would preserve source's global uniqueness.)
So the only remaining strictly compliant solution is to encode entire string into sequence of digits. This means that the target "number" becomes just another representation of given string, and as a consequence the original string would be fully re-constructible from given number.
Caveats
BigInt is limited by operation memory, most probably not very suitable for really large input strings and/or constrained environments.