Reputation: 25277
I want to have typescript assert that a 'new' string is not part of a union, an existing set the string will be added to.
I'm looking for an independent, standalone line of code, but that's just a nice-to-have.
So, the simple, albeit contrived situation:
type FruitOptions = 'banana' | 'apple' | 'grape'
type NewFruitOption = 'pear'
// ^ being passed into a function ^
// Assert: FruitOptions does not contain NewFruitOption
I don't think sharing this is necessary, but this is the utility function I'm seeking to employ this technique on:
// These are inferred generics (since they come after the = sign), do not pass them in.
export const renameKey = <
OldKey extends keyof T,
NewKey extends string, // should NOT already be keyof T
T extends Record<string, unknown>
>(
oldKey: OldKey,
newKey: NewKey,
userObject: T
): Record<NewKey, T[OldKey]> & Omit<T, OldKey> => {
const { [oldKey]: value, ...common } = userObject
return {
...common,
...({ [newKey]: value } as Record<NewKey, T[OldKey]>)
}
}
Upvotes: 0
Views: 168
Reputation: 35512
You can check if the NewKey
extends keyof T
, and if it does, set the parameter type to never
so typescript will error:
newKey: NewKey extends keyof T ? never : NewKey
When you then change this valid call:
renameKey('lastName', 'clientLastName', formData)
To be invalid:
renameKey('lastName', 'lastName', formData)
You'll get a red underline on the second 'lastName'
saying: Argument of type 'string' is not assignable to parameter of type 'never'. ts(2345)
. It will also fail if you try to rename the key any other key that already exists:
renameKey('lastName', 'dateOfBirth', formData)
Upvotes: 1