Search code examples
typescriptconditional-types

How to remove a key/property from a function type


How do I remove name from the following function type:

type Func = {
  (): number
  name: string
}

Omit<Func, 'name'> result in never.

Playground link


Solution

  • You can't really do this because name is part of the special built-in Function interface, which all callables in TypeScript inherit from. As soon as the compiler sees that a type is callable, it will have a name (and a bind and a call and an apply, etc). You won't be able to widen the type to remove those keys.

    See microsoft/TypeScript#27575 for the canonical issue about this. I don't know that anything will happen there, but that's where you should go to describe your use case and give a 👍 if you want to increase the chance of it being addressed.

    Anyway, the closest you will be able to get here is to have name be of type never, which is a narrowing and not a widening:

    type MyFunc = {
      (): number;
      readonly name: never;
    };
    

    You can still call it:

    declare const f: MyFunc;
    const num = f(); // okay
    

    And while it does have a name:

    f.name; // no error here, but
    

    That name is no longer seen as a usable string type:

    f.name.toUpperCase(); // error
    // Property 'toUpperCase' does not exist on type 'never'.
    

    If, you were talking about a different property that's not built-in to Function, like

    type MyFuncTwo = {
      (): number;
      title: string;
      age: number;
    }
    

    then you can remove those but not with Omit<>, which is a mapped type. Mapped types skip call/construct signatures. There's an open issue for that too: microsoft/TypeScript#29261. To work around that, you'd need to make your own type mapper that re-adds the call signature:

    type MyOmit<T, K extends PropertyKey> =
      (T extends (...args: infer A) => infer R ? (...args: A) => R : unknown) & Omit<T, K>;
    

    That works for the example above:

    type MyFuncThree = MyOmit<MyFuncTwo, "title">;
    // type MyFuncThree = (() => number) & Pick<MyFuncTwo, "age">
    

    but there are all sorts of edge cases around overloads and maybe generics. If you really want to see a non-workaround you might want to go to #29261 and give it a 👍 and describe your use case.


    Playground link to code