8

Lets say we have this Type:

export type UsersSchema = {
  id: number;
  firstName: string;
  lastName: string;
  email: string;
};

Is there a way to make this pseudo-code work:

Object.keys(UsersSchema) which would ideally yield: ['id', 'firstName', 'lastNight', 'email']

Obviously UsersSchema is not a value, so the above does not work...

benhowdle89
  • 36,900
  • 69
  • 202
  • 331

3 Answers3

8

The type doesn't exist at run time.

However, (leaning heavily on this beautiful answer, which will require TS4.x because of its use of recursive conditional types), you can create a tuple type that enforces a tuple with the required names.

So:

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


export type UsersSchema = {
  id: number;
  firstName: string;
  lastName: string;
  email: string;
};

const allKeysOfUsersSchema: TupleUnion<keyof UsersSchema> = 
    ["id", "firstName", "lastName", "email"]; //OK

const wrongKeysOfUsersSchema: TupleUnion<keyof UsersSchema> = 
    ["monkey", "firstName", "lastName", "email"]; //error

const missingKeysOfUsersSchema: TupleUnion<keyof UsersSchema> = 
    ["id", "firstName", "lastName"]; //error

const tooManyKeysOfUsersSchema: TupleUnion<keyof UsersSchema> = 
    ["id", "firstName", "lastName", "email", "cat"]; //error

OK... so you'll have to maintain this separate tuple, manually, but at least if things change, the compiler will push you into remedial action, so type-safety is maintained.

Playground link

spender
  • 117,338
  • 33
  • 229
  • 351
3

You can use keyof type operator.

export type UsersSchema = {
  id: number;
  firstName: string;
  lastName: string;
  email: string;
};

type Keys = keyof UsersSchema // "id" | "firstName" | "lastName" | "email"
hwasurr
  • 31
  • 2
  • Is there a way to use them as value now? `fetch("https://my-url?filter=id,firstName,lastName,email")`? – Matthis Kohli Mar 21 '22 at 12:57
  • @MatthisKohli As far as i know, it's impossible to use type as a value. However, you can use this key type to create type-checking functions – hwasurr Mar 25 '22 at 00:16
3

in addition to hwasurr answer you probably need to put the keys in parenthesis to get the right type

the array of keys:

type Keys = (keyof UsersSchema)[]
// ("id" | "firstName" | "lastName" | "email")[]