Shady Goat
Shady Goat

Reputation: 73

Get keys of an interface, in generics

I have a function that uses generics. The generics in question is just <T extends anInterfaceName>. The problem is, in the function I need to get the keys of the interface used in T. After doing some research, i found this module.

Problem is, it doesn't work with generics. I can't seem to find anything that can help, is there anything that could do this? Is there a work around to this problem?

Upvotes: 1

Views: 3448

Answers (2)

Here you have several wayt to get keys:

interface Keys {
  name: string;
  age: number;
}

const fun1 = <T,>(obj: T, key: keyof T): T[keyof T] => null as any
const fun2 = <T, K extends keyof T>(obj: T, key: K): T[K] => null as any
const fun3 = (key: keyof Keys) => null
const fun4 = <T extends Keys, K extends keyof T>(obj: T, key: K) => null 

If above code does not work for you, please provide an example what are you expect.

UPDATE

/**
 * UPDATE
 */

const fun5 = <T,>(): Array<keyof T> => null as any
const fun6 = <T,>(): (keyof T)[] => null as any

const result = fun5<Keys>() // ("name" | "age")[]

UPDATE 2

If you want more strict type of array, you can use next util:


//Credits goes to https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type/50375286#50375286
type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never

// Credits goes to ShanonJackson https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type UnionToOvlds<U> = UnionToIntersection<U extends any ? (f: U) => void : never>;

// Credits goes to ShanonJackson https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type PopUnion<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? A : never;

// Credit goes to Titian Cernicova-Dragomir  https://stackoverflow.com/questions/53953814/typescript-check-if-a-type-is-a-union#comment-94748994
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true

// Finally me)
type UnionToArray<T, A extends unknown[] = []> = IsUnion<T> extends true ? UnionToArray<Exclude<T, PopUnion<T>>, [PopUnion<T>, ...A]> : [T, ...A]

interface Person {
  name: string;
  age: number;
  surname: string;
  children: number;
}

type Result = UnionToArray<keyof Person>


const func = <T,>(): UnionToArray<keyof T> => null as any

const result = func<Person>() // ["name", "age", "surname", "children"]

I really don't know other wayt to get keyof's.

Please keep in mind, this solution is not CPU friendly and there is no order guarantee.

UPDATE 3

Here is much safer option to obtain keys. Next solution takes into account that order could not be preserved:

type TupleUnion<U extends string, R extends any[] = []> = {
    [S in U]: Exclude<U, S> extends never ? [...R, S] : TupleUnion<Exclude<U, S>, [...R, S]>;
}[U];

interface Person {
    firstName: string;
    lastName: string;
    dob: Date;
    hasCats: false;
}

type keys = TupleUnion<keyof Person>;//

Shamelessly stolen from Wroclaw twitter TS group

Upvotes: 1

Jose Greinch
Jose Greinch

Reputation: 458

I think you are using Typescript in the wrong way.

If you are really interested in going that path you probably can use Typeguards.

https://www.typescriptlang.org/docs/handbook/advanced-types.html

but the problem here is that anInterfaceName needs to have the keys of what you are interested to grab, T extends anInterfaceName means I can be anything that has the anInterfaceName keys, so there's an infinite possibilities of T classes..

Upvotes: 0

Related Questions