Skip to content

Except not playing well conditional types and/or infer #1421

Description

@gt-hive-benedict-lee

Bug description

I've been using type-fest to replace some of the type utilities we have in our project, but when trying to use Except to replace our alternative version of Omit (which we needed because Omit loses known keys) I was getting errors due to the keyof ObjectType requirement in the keys. This doesn't seem to play well with conditional types and/or infer

For conditional types:

type Cond<T> =
  T extends string ? {
    name: string;
    remove: string
  }
  : T extends number ? { prime: boolean }
  : {}


type Removed<T> = Except<Cond<T>, "remove">
//                                  ^ ---> Type 'string' does not satisfy the constraint 'keyof A<T>'.
//                                          Type '"remove"' is not assignable to type '("remove" | "name") & keyof (T extends number ? { prime: boolean; } : {})'.

For infer, I've been working with the CompnentProps type from Vue, which is defined as:

type ComponentProps<T> =
  T extends new (...args: any) => {
    $props: infer P
  } ? NonNullable<P>
  : T extends (props: infer P, ...args: any) => any ? P
  : {}

Trying to use Except with it runs into

type ComponentPropsLessKey<T> =
  Except<ComponentProps<T>, "key">
//                          ^ ---> Type 'string' does not satisfy the constraint 'keyof ComponentProps<T>'.
//                                  Type '"key"' is not assignable to type 'never'.

This seems to imply that keyof ComponentProps<T> resolves to never

I have a type that attempts to get the user defined props from a Vue component, and I get a similar type error as the above when attempting to use Except to define it:

type ComponentPropsUser<T> = Except<
  ComponentProps<T>,
  keyof VNodeProps
  | keyof AllowedComponentProps
  | keyof ComponentCustomProps // This is an empty interface by default as it is meant for extension by users
>
// ^ ---> Type 'string' does not satisfy the constraint 'keyof ComponentProps<T>'.
//  Type 'string' is not assignable to type 'keyof ComponentProps<T>'.
//    Type '"key"' is not assignable to type 'never'.

This might be better resolved by a version of Except that would be less strict in the types of the keys it expects, but I will leave it up to the project to decide if this is an issue worth fixing.

For reference, I currently use an alternative version of Omit defined as:

// Taken from https://stackoverflow.com/a/65673414
type AlternativeOmit<T, K extends PropertyKey> = {
  [P in keyof T as Exclude<P, K>]: T[P]
};

Repro

https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBDAnmApnA3nAogDwMYpjwC+cAZlBCHAORKoC0ZKAzjDQNwCwAUKJLATI0mAMJVIAOxSSYABUpgWcUhSpwARADcArigZ4JEabIb19ACxQAbVFBYbuPXubjjJAEwA8AFQB8cAC8vHBwPnAoODAyHspsUMCSAOZwAPwYIaFwkgCGICgAXHDxiUlOWXBQKCAQWoXFMAnJmcSZReGR0Z7KkjogAEYoUGkYcGAJ+UX9EBDWKDmSKm0Yrc5rrgBK1bUo3v5B2PiEMF7ue34ANJpVNXUafrwuwm5GJvKKLAAyrCwA0iiIXwBYI8UK4AhEU6vGTvCBKIFXDQAawB90ePFc4nAxhhCjhLAAqiwhkCDuDjl5MlipLiPgjMijEBAyHAAGoAOQgHhQeKUmQAPnBGcy4ABBazWCAAd121Jxsl5LAFQoBIrlb1EOjYVEVvD8HCAA

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions