2

With function which returns values from object with some list of keys from the object like below.

function mapObjToArray(obj, keys) {
    return keys.map(key => obj[key])
}

const result = mapObjToArray({ A: "a", B: "b", C: 'c', X: 'x'}, ['A', 'B'])

I want get exact type of result value, I mean ["a", "b"] in this case, but, with code below, type of result is ("a" | "b" | "c" | "x")[] instead of ["a", "b"].

function mapObjToArray<T>(obj: T, keys: (keyof T)[]) {
    return keys.map(key => obj[key])
}

const result = mapObjToArray({ A: "a", B: "b", C: 'c', X: 'x'} as const, ['A', 'B'])

I may advanced little with Variadic Tuple Type and I got...

function mapObjToArrayWithVariadicTuple<T, X extends [...(keyof T)[]]>(obj: T, keys: X): [...T[X[number]][]] {
    return keys.map(key => obj[key])
}

const resultVariadicTuple = mapObjToArrayWithVariadicTuple({ A: "a", B: "b", C: 'c', X: 'x'} as const, ['A', 'B'])

Getting closer, but still, typeof resultVariadicTuple is not I wanted ("a" | "b")[]

Is there any way to get the result type I want???

TypeScript Playground

1
  • @tom10271 While that's true, I think we can assume for the purposes of the question that the value (and type) being passed in is static Commented Jun 15, 2022 at 2:56

2 Answers 2

1

One sure thing is that both obj and keys parameters must be const. Otherwise, as types in TypeScript are detached from the runtime, the returned type is undeterminable.

If this requirement can be assumed, then something like this should be the answer:

type _Map<
  K extends readonly any[],
  O extends { [key in K[number]]: any }
> = K extends readonly []
  ? []
  : K extends readonly [infer H, ...infer T]
  ? [O[H], ..._Map<T, O>]
  : never;

function mapObjToArray<
  K extends readonly (string | number | symbol)[],
  O extends { [key in K[number]]: any }
>(obj: O, keys: K): _Map<K, O> {
  return keys.map((key: K[number]) => obj[key]) as any;
}

const result = mapObjToArray(
  { A: "a", B: "b", C: "c", X: "x" } as const,
  ["A", "B"] as const
);
Sign up to request clarification or add additional context in comments.

Comments

1

I hope it helps

//////////////////////////////////////////////////////////////
// This is where the magic happens
type Tobj<O> = {
  [key in keyof O]: [key, O[key]];
};
type ObjectToTuple<O> = Tobj<O>[keyof Tobj<O>];
//////////////////////////////////////////////////////////////


const test = {
  a: "a",
  b: "b",
  c: 1
} as const;

type T = typeof test;

type TupleT = ObjectToTuple<T>;

(Object.entries(test) as TupleT[]).map((entry) => {
  switch(entry[0]){
    case "a":{
      return entry[1] === "a"; // TRUE!
    }
    case "b":{
      return entry[1] === "b"; // TRUE!
    }
    case "c":{
      return entry[1] === 1; // TRUE!
    }
  }
});

Note that you still have to cast the array because typescript cannot guarantee you that the object doesn't have more attributes than the one listed in the type

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.