Home > other >  Typescript generic function to iterate over type keys
Typescript generic function to iterate over type keys

Time:10-03

New to Typescript... is it possible to write a generic function in typescript which will print the keys of a given type? Something like:

const getKeys = <T>(): string[] => { ??? }

interface A {
  a: string,
  b: number
}

getKeys<A>(); // returns ["a", "b"]

Rationale: I want to write a function which will get a raw object and a type and will check if it contains all the keys from that type. I don't want to pass the keys for every type!

const checkKeys = <T>(rawData: any): boolean => { ??? }

interface A {
  a: string,
  b: number
}

const wrong = {
  x: "aaa",
  y: "bbb",
};

checkKeys<A>(wrong); // return false

I can write something like this:

const checkKeys = <T, K = keyof T>(data: any, ...keys: K[]) => {
  for (const key of keys) {
    if (!data[key]) {
      return false;
    }
  }
  return true;
}

interface A {
  a: string,
  b: number,
}

const right = { a: "a", b: "b" };
const wrong = { c: "a", b: "b" };

checkKeys<A>(right, "a", "b")); // returns true
checkKeys<A>(wrong, "a", "b")); // returns false

But I always have to pass the keys for every type I want to call it on, really annoying!

I know that Typescript is just the type system and we need something at runtime, but is there a way that Typescript can generate the values for keys at compile time since it knows them in my case and even can check that I am passing only the acceptable values for keys parameter? If it is that smart during compile time, it could generate an array for me when compiling to Javascript. Is there a way that I'm not aware of?

CodePudding user response:

Typescript won't generate code for you.

The closest thing you could have is creating the possible keys in advance :

const keys = ['a', 'b', 'c'] as const;
type Keys = typeof keys[number] // 'a' | 'b' | 'c'

type Foo = Record<Keys, string>; 

const foo: Foo = {
  a: 'foo',
  b: 'bar',
  c: 'baz',
}

Here keys is a array you can iterate over.

CodePudding user response:

const sample = {
  a: '123',
  b: 123
}

type Dict = { [key: string | number]: any}
type CheckKeys<T extends Dict, S extends Dict> = keyof T extends never ? false : (keyof S extends keyof T ? true : false)

function checkKeys<
  T extends Dict,
  S extends Dict,
>(data: T, smp: S): CheckKeys<T, S> {
    const smpKeys = Object.keys(smp)
    for (const key in data) {
        if (!(key in smpKeys)) return false as CheckKeys<T, S>
    }
    return true as CheckKeys<T, S>
}

const right = { a: "a", b: "b" };
const wrong = { c: "a", b: "b" };

// const checkRight: true
const checkRight = checkKeys(right, sample);

// const checkWrong: false
const checkWrong = checkKeys(wrong, sample);
  • Related