0

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]>)
  }
}

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.