Reputation: 3783
In the following code, I am trying to create a new type from an existing type, by looping through the keys and replacing only the ones that match the condition.
I am also using union types here.
class A {}
class B {
constructor(public a: A, public n: number, public aa: A[]) {}
}
type X = A | B
type ReplaceKeyTypes<Type extends X, NewKeyType> = {
[Key in keyof Type]: Key extends X ? NewKeyType : Type[Key]
}
const a: A = new A()
const b: B = new B(a, 1, [a, a])
const c: ReplaceKeyTypes<B, string> = {
a: 'test',
n: 2,
aa: ['xyz']
}
This gives me the following errors in the last few lines of code:
My questions are:
c.n
get changed to a string, when the original key type is a number which does not satisfy "Key extends X
"?c.aa
should change from X[]
to string[]
Upvotes: 1
Views: 5767
Reputation: 6462
This type allows omitting and replacing in one step, if all keys you're replacing will have the same type:
type ReplaceKeys<Type, Keys extends string, NewType> = Omit<Type, Keys> & {
[key in Keys]: NewType
}
// use:
type B = ReplaceKeys<A, 'k2' | 'k4', 'replaced'>
const b: B = {
k1: 0,
k2: 'replaced',
k3: [],
k4: 'replaced'
}
Or, if you want to overwrite multiple keys of various types at once:
type Overwrite<A, B> = Omit<A, keyof B> & B
// use:
type C = Overwrite<A, {k1: 'replaced', k2: 'also replaced'}>
const c: C = {
k1: 'replaced',
k2: 'also replaced',
k3: [''],
k4: true
}
Note that for simplicity, I did not make it require the new keys to exist on the original type.
Upvotes: 0
Reputation: 3303
type A = {
a: string;
b: string;
c: string;
};
type B = Omit<A, "b" | "c">;
type C = Omit<A, "b" | "c"> & { b: number; c: number };
Now, if you hover over B
you will see this:
type B = {
a: string;
}
And type C
has been this:
type C = {
a: string;
b: number; // Replaced
c: number; // Replaced
};
Upvotes: -1
Reputation: 13289
I'm not sure this would work exactly as is with multiple keys you want to replace, but if you have just one key you want to "re-type" I've done:
type NewType = Omit<OldType, 'keyToChange'> & {
keyToChange: NewTypeOfKey;
};
Haven't taken the time to genericize this but it would probably be pretty easy.
Upvotes: 3
Reputation: 3783
It seems when doing Key in keyof Type
you get the literal key, i.e. the string that names the key. I had forgotten to get the key's value type by using Type[Key]
. To fix, and to support the array case, here's what I came up with:
type ReplaceKeyTypes<Type extends X, NewKeyType> = {
[Key in keyof Type]: Type[Key] extends X[]
? NewKeyType[]
: Type[Key] extends X
? NewKeyType
: Type[Key]
}
Upvotes: 3