Devin Rhode
Devin Rhode

Reputation: 25277

typescript: assert string is not part of a union

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

Answers (1)

Aplet123
Aplet123

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)

error shown inside vscode

Upvotes: 1

Related Questions