'Make typescript interface extend another interface excluding keys already defined

For some hacky scripting on my iPhone (not code I would ever write for production), I frequently find myself treating strings as string[] (a character array). Since typing on a phone is significantly slower than on a keyboard and characters are at a premium given the 57-character-wide screen size, I wrote some code that takes every method of Array.prototype, maps it to Array.from(this)[fn], and attaches it to String.prototype if it does not already exist. For example, I want .slice() still to return a string, not a string[].

Object.entries(
  Object.getOwnPropertyDescriptors(Array.prototype)
)
  .filter(([n, d]) =>
    !(n in String.prototype) &&
    typeof d.value === 'function'
  )
  .forEach(([n]) =>
    String.prototype[n] = function (...args) {
      return Array.from(this)[n](...args)
    }
  )

Now I can run 'abc'.filter(x => x !== 'b') and receive ['a', 'c']. Huzzah.

Now I want to write the updated interface String without manually copying over every valid declaration from Array<T>. (Sometimes play.js will read a .d.ts file.)

My best attempt introduces circular type references, which doesn't surprise me.

type MethodNames<T> = {
  [K in keyof T & string]: T[K] extends (...args: any[]) => any ? K : never;
}[keyof T & string];

type MethodsOf<T> = {
  [K in MethodNames<T>]: T[K];
};

type CharArray = Omit<MethodsOf<Array<string>>, keyof string>;

declare global {
  interface String extends CharArray {}
}

export {};

TS Playground

Up until I make String extend CharArray, everything works fine. CharArray has the proper methods. Once I add the extension, CharArray tries to exclude its own members, since those members are now members of String.

Is what I'm asking even possible? I suspect the answer is no, but I thought I would ask.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source