I have a type and I would like to create another type from it in which one field is renamed to another. E.g., I have a type like
type A = {
name: 'A'
A: number
}
type B = {
name: 'B'
B: number
}
type Before = A | B
and I want to rename the field name
in Before
to name2
.
At first. I thought about something like
type After = Omit<Before, 'name'> & { name2: Before['name'] }
const a: After = {
name2: 'A'
A: 2
}
This is also the answer I saw in some similar question here.
But then I get an error when defining a
.
Type '{ name2: "A"; A: number; }' is not assignable to type 'After'.
Object literal may only specify known properties, and 'A' does not exist in type 'After'.(2322)
CodePudding user response:
I think it's a distribution problem, which we can solve one of two ways:
- With a mapped type
- By working directly from the source types
A
andB
directly, it works as expected:
Mapped type
We can create a utility type to rename a key:
type Rename<ObjectType, FromKey extends PropertyKey, ToKey extends PropertyKey> = {
[Key in keyof ObjectType as Key extends FromKey ? ToKey : Key]: ObjectType[Key];
}
Like Omit
, that will be distributed across the A | B
union, giving us the result we want:
type After = Rename<Before, "name", "name2">;
const a: After = {
name2: "A",
A: 2,
};
const b: After = {
name2: "B",
B: 2,
};
From the source types
But if you didn't really need Before
, you could just define After
directly from A
and B
using Omit
as you've shown:
type A = {
name: "A";
A: number;
};
type B = {
name: "B";
B: number;
};
type Before = A | B;
type After =
| (Omit<A, "name"> & { name2: A["name"] })
| (Omit<B, "name"> & { name2: B["name"] });
const a: After = {
name2: "A",
A: 2,
};
const b: After = {
name2: "B",
B: 2,
};