Irakli Safareli

Non-string keys in object literal syntax JS


At one point I was thinking on how one could use object literal syntax ({foo: 1, baz: true}) to create Map tho use keys which are non-string values, number, object etc and wrote this code which kinda works. tho probably no one’s gonna use it including me :D

type KeyStore<T> = {
  asKey: (value: T) => symbol;
  fromKey: (key: symbol) => [] | [T];
};

const mkKeyStore = <T>(): KeyStore<T> => {
  const map = new Map<symbol, T>();
  const asKey = (value: T): symbol => {
    const key = Symbol();
    map.set(key, value);
    return key;
  };

  const fromKey = (key: symbol): [] | [T] => {
    if (map.has(key)) {
      return [map.get(key)!];
    }
    return [];
  };
  return { asKey, fromKey };
};

const mapMakerMaker =
  <K>(s: KeyStore<K>) =>
  <T>(
    record: Record<Extract<K, string | number | symbol> | symbol, T>
  ): Map<K, T> => {
    const stringEntires: [string, T][] = Object.entries<T>(record);
    const entires1 = stringEntires as any as [K, T][];

    const symbols = Object.getOwnPropertySymbols(record);
    const entries2 = symbols.flatMap((key) =>
      s.fromKey(key).map((v): [K, T] => [v, record[key]])
    );
    const map = new Map<K, T>([...entires1, ...entries2]);
    return map;
  };

const keyStore = mkKeyStore<string | ["required", string]>();
const required = (key: string) => keyStore.asKey(["required", key]);
const mkMap = mapMakerMaker(keyStore);

const x = mkMap({
  [required("name")]: "string",
  age: "int",
});

console.log(x);
// Map (2) {"basd" => "int", ["required", "asd"] => "string"}