I have an object of type A
, that I wish to convert into type B
. The easiest way with basic immutability is to shallow clone this object and delete the extraneous properties.
type A = B & {
prop1;
}
type B = {
prop2;
}
const example = (oldA: A): B => {
const newB = { ...oldA };
delete newB.prop1;
return newB as B;
}
Without assigning the type using as
, TypeScript doesn't understand the return value is the correct return type.
I don't like casting with as
whenever possible as this is very error prone. If I assign the temporary variable as type A
, then I cannot delete without getting an error
const newB: A = { ..oldA }
The operand of a 'delete' operator must be optional
After reading online, I see I can use this notation;
const example = (oldA: A): B => {
const newB: Partial<Pick<A, 'prop1'>> & Omit<A, 'prop1'> = { ...oldA };
delete newB.prop1;
return newB;
}
This works nicely, but I want to reuse this as a generic. I have tried the generic type below but I get two errors
type PartialProperty<X, Y> = Partial<Pick<X, Y>> & Omit<X, Y>
Type 'Y' does not satisfy the constraint 'keyof X'
Type 'Y' does not satisfy the constraint 'string | number | symbol'
How can I correctly write the TypeScript definition as a reusable generic?
CodePudding user response:
The Pick<T, K>
utility type requires that K
be assignable to keyof T
, and the Omit<T, K>
utility type requires that K
be assignable to some keylike type (string | number | symbol
, also known as PropertyKey
).
The problem with your PartialProperty
definition is that your second, "key" argument (which you are calling Y
but I will call K
because that's more conventional) is not constrained at all. You can't pass it as the second type argument to Pick
or Omit
unless you constrain it to the appropriate key type. In a generic type parameter declaration like type Foo<T> = ...
you can add a constraint like type Foo<T extends XXX> = ...
so that any type argument passed in a T
must be assignable to XXX
.
Here's how it looks for PartialProperty
:
type PartialProperty<X, K extends keyof X> =
Partial<Pick<X, K>> & Omit<X, K>
That compiles without error because now K
is constrained to keyof X
, which satisfies both Pick
(since K
extends keyof X
) and Omit
(since K
extends keyof X
which extends PropertyKey
).