Eduard
Eduard

Reputation: 9165

Rename key of typescript object type

I have this:

type Two = {
  one: number,
  two: string,
  three: boolean
}

I want it to create a type that would look like this:

type RenamedTwo = {
  one: number,
  two: string,
  four: boolean // difference
}

Tried to create it this way:

type Rename<T, K extends keyof T, N> = Pick<T, Exclude<keyof T, K>> & { [N]: T[K] }

In an attempt to use this way:

type Renamed = Rename<Two, 'three', 'four'>

But TSlint marks [N] as error and gives this error message:

[ts] A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type. [ts] 'N' only refers to a type, but is being used as a value here.

Upvotes: 19

Views: 23886

Answers (4)

Anthony
Anthony

Reputation: 436

I had a similar need but I wanted to be able to rename as many properties of an object that I wanted to so I came up with this function where I pass the object to rename and the associated mapping:

type Mapped<
  Type extends object,
  Mapping extends Partial<Record<keyof Type, string>>,
> = {
  [Property in keyof Type as Property extends keyof Mapping
    ? Mapping[Property] extends string
      ? Mapping[Property]
      : Property
    : Property]: Type[Property];
};

export function renameProperties<
  Type extends object,
  Mapping extends Partial<Record<keyof Type, string>>,
  Result extends Mapped<Type, Mapping>,
>(obj: Type, mapping: Mapping): Result {
  return Object.entries(obj).reduce<Result>((acc, [key, value]) => {
    const newKey = mapping[key] ?? key;
    return {
      ...acc,
      [newKey]: value,
    };
  }, {} as Result);
}

I used the remapping ability shared by @banana :)

Upvotes: 0

banana
banana

Reputation: 151

In the current typescript version 4.6.2, there is a remapping gramma to use. It can be implement this much easier.

PlayGround

type RenameByT<T, U> = {
  [K in keyof U as K extends keyof T
    ? T[K] extends string
      ? T[K]
      : never
    : K]: K extends keyof U ? U[K] : never;
};

type Two = { one: number; two: string; three: boolean };

// returnType = { one: number; two: string, four: boolean };
type renameOne = RenameByT<{three: 'four', five: 'nouse'}, Two>;

// returnType = { x: number, y: string, z: boolean; }
type renameAll = RenameByT<{one: 'x', two: 'y', three: 'z'}, Two>;

The RenameByT type can separate by several parts. The explain is for the example renameOne

  1. K in keyof U means all the key in U. For the example is one | two | three
  2. as clause since ts4.1 can use. but i can't use in 4.4.4 but can use in 4.6.2. This is use to rename Key K by the condiction type
  3. K extends keyof T. keyof T means three | five.
  4. T[K] extends string means T[K] is string. T['three'] is string so returns four, T['five'] returns nouse;
  5. the return type K extends keyof U, so T['three'] is satisfy so return { four: U['three'] } means { four: boolean}
  6. others keys return as origin

reference:

https://github.com/microsoft/TypeScript/issues/40833 https://www.typescriptlang.org/docs/handbook/2/mapped-types.html

Upvotes: 15

Tom Greenwood
Tom Greenwood

Reputation: 1672

It is perhaps worth mentioning that you can solve the OP's original problem without writing a generic type like Rename. If you just want to do it once, you could do something much simpler.

type RenamedTwo = { [Property in keyof Two as Property extends 'three' ? 'four': Property]:  Two[Property] }

Upvotes: 3

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249656

You need to use a mapped type for the renamed property as well:

type Two = {
    one: number,
    two: string,
    three: boolean
}


type Rename<T, K extends keyof T, N extends string> = Pick<T, Exclude<keyof T, K>> & { [P in N]: T[K] }

type Renamed = Rename<Two, 'three', 'four'>

Note that this will not work as expected if you provide more properties:

type Renamed = Rename<Two, 'two'  |'three' , 'four' | 'five'> // will be Pick<Two, "one"> & {
//    four: string | boolean;
//    five: string | boolean;
// }

Upvotes: 22

Related Questions