Reputation: 73
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
Reputation: 33051
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
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