Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Pick and rename certain keys using Typescript?

Tags:

typescript

I have an interface User:

interface User {
    _id     : string;
    name    : string;
    email   : string;
    password: string;
    phone   : number;
}

I have another interface UpdatedBy:

interface UpdatedUser {
    id  : string;
    name: string;
}

I know I can use Pick, but I want to rename _id to id in the UpdatedUser interface.

type UpdatedUser = Pick<User, '_id' | 'name'>; // How can I turn _id into id?

Update: I basically want to do a cleaner version of this:

export interface UpdatedUser extends Pick<User, 'name'> {
    id  : Extract<User, '_id'>;
}
like image 358
yaserso Avatar asked Nov 27 '19 13:11

yaserso


People also ask

How do you change the key name of an object in TypeScript?

Syntax: obj['New key'] = obj['old key']; Note: Renaming the object by simple assignment of variable could be applied on multiple key, value pairs.

How do I rename a properties in TypeScript?

Add a typescript property. Select it and press F2 (or right click and select rename from the menu)

How do I change the key name of an object?

To rename a key in an object:Use bracket notation to assign the value of the old key to the new key. Use the delete operator to delete the old key. The object will contain only the key with the new name.

What is keyof in TypeScript?

keyof is a keyword in TypeScript which is used to extract the key type from an object type.


1 Answers

There is no built-in type for a renaming Pick, fortunately we can create one with reasonable effort.

Simple variant

type IdRenamed = Omit<User, "_id"> & { id: User["_id"] }
// { name: string; email: string; password: string; phone: number; id: string;}

Playground

Dynamic version for single property

type PickRename<T, K extends keyof T, R extends PropertyKey> =
    Omit<T, K> & { [P in R]: T[K] }

type T21 = PickRename<User, "_id", "id"> // same type as above
type T22 = PickRename<User, "foo", "id"> // error, foo is no property

Playground

TS 4.1 Alternative: use mapped type as clauses. Its advantage is that readonly or optional (?) modifiers of properties are preserved (see homomorphic mapped types 1, 2 for more details).

type PickRename<T, K extends keyof T, R extends PropertyKey> = {
    [P in keyof T as P extends K ? R : P]: T[P]
} // type instantiation same as previous example

Playground

Dynamic version for multiple properties

type PickRenameMulti<T, R extends
    { [K in keyof R]: K extends keyof T ? PropertyKey : "Error: key not in T" }
    > = Omit<T, keyof R> & UnionToIntersection<
        { [P in keyof R & keyof T]: { [PP in R[P]]: T[P] } }[keyof R & keyof T]
    >

type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends
    ((k: infer I) => void) ? I : never

type T31 = PickRenameMulti<User, { _id: "id"; name: "firstName" }>
type T32 = PickRenameMulti<User, { foo: "id" }> // error, foo is no property

Note: See the great UnionToIntersection type for more details on the helper.

Playground

TS 4.1 again eases up syntax and produces homomorphic mapped types:
type PickRenameMulti<T, R extends
    { [K in keyof R]: K extends keyof T ? PropertyKey : "Error: key not in T" }
    > = { [P in keyof T as P extends keyof R ? R[P] : P]: T[P] }

Playground

TS 4.1: Drop _ prefix from all property keys

type DropUnderscore<T> = {
    [K in keyof T as K extends `_${infer I }` ? I : K]: T[K]
};
type T4 = DropUnderscore<User> // "_id" and "_email" renamed to "id", "email"

Playground

like image 79
ford04 Avatar answered Sep 19 '22 06:09

ford04