Skip to content

Instantly share code, notes, and snippets.

@atian25
Last active December 30, 2022 08:59
Show Gist options
  • Save atian25/5286b911907a9a382d352ae8104a5bd3 to your computer and use it in GitHub Desktop.
Save atian25/5286b911907a9a382d352ae8104a5bd3 to your computer and use it in GitHub Desktop.
// https://tsplay.dev/WG4bKw
class TestRunner {
plugin(plugins) {
for (const key of Object.keys(plugins)) {
const initFn = plugins[key];
this[key] = (...args) => {
initFn(this, ...args);
return this;
};
}
return this;
}
end() {
console.log('done');
}
}
// test case
const test_plugins = {
stdout(runner: TestRunner, expected: string) {
console.log('stdout', expected);
},
code(runner: TestRunner, expected: number) {
console.log('code', expected);
},
};
new TestRunner()
.plugin({ ...test_plugins })
.stdout('a test')
// should invalid, expected is not string
.stdout(/aaaa/)
.code(0)
.end();
// invalid case
const invalidPlugin = {
// invalid plugin, the first params should be runner: TestRunner
xx: (str: string) => {
console.log('### xx', typeof str);
},
};
new TestRunner()
.plugin({ ...test_plugins, ...invalidPlugin })
.stdout('a test')
.xx('a test')
.code(0)
.end();
@atian25
Copy link
Author

atian25 commented Dec 30, 2022

type ExtractFn<T> = {
  [P in keyof T]: T[P] extends (arg0: TestRunner, ...restArgs: infer A) => void ? (...args: A) => TestRunner & ExtractFn<T> : never
}

class TestRunner {
  // TODO: write a gymnastics for this.
  plugin<T extends Record<string, (arg0: TestRunner, ...restArgs: any[]) => unknown>>(plugins: T) {
    for (const key of Object.keys(plugins)) {
      const initFn = plugins[key];
      ;(this as any)[key] = (...args: unknown[]) => {
        initFn(this, ...args);
        return this;
      };
    }
    return this as ExtractFn<T>;
  }
  end() {
    console.log('done');
  }
}

// test case
const test_plugins = {
  stdout(runner: TestRunner, expected: string) {
    console.log('stdout', expected);
  },
  code(runner: TestRunner, expected: number) {
    console.log('code', expected);
  },
};

new TestRunner()
  .plugin({ ...test_plugins })
  // should know the type of arg1
  .stdout('a test')
  // should invalid, expected is not string
  .stdout(/aaaa/)
  .code(0)
  .end();

// invalid case
const invalidPlugin = {
  // invalid plugin, the first params should be runner: TestRunner
  xx: (str: string) => {
    console.log('### xx', typeof str);
  },
};

new TestRunner()
  .plugin({ ...test_plugins, ...invalidPlugin })
  .stdout('a test')
  .xx('a test')
  .code(0)
  .end();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment