-
Notifications
You must be signed in to change notification settings - Fork 1.4k
support test macros in typescript definitions #975
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
support test macros in typescript definitions #975
Conversation
@ChristianMurphy I'll try looking into it this evening or tomorrow. If not, don't hesitate to ping me back in a couple of days, quite busy lately :). |
types/make.js
Outdated
output += '\t' + writeFunction(part, 'name: string', 'void'); | ||
} else { | ||
const type = testType(parts); | ||
output += '\t' + writeFunction(part + '<I, E>', 'implementation: Macro<I, E, ' + type + 'Context>, input: I, expected: E'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't allow multiple macro's (like in the test
function in base.d.ts
). I would define a type alias, after the definition of Macro in base.d.ts
:
type Macros<I, E, T> = Macro<I, E, T> | Macro<I, E, T>[];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you put the macro declarations after the original declarations? Editors will then first show the macro-less declaration, and that is probably the most used one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done and Done
79a5080
to
2beeebb
Compare
Looks good to me now. @SamVerschueren have you taken a look at this? |
types/base.d.ts
Outdated
context: any; | ||
} | ||
|
||
export interface Macro<I, E, T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I would put the T
argument at the first position. Doesn't it make more sense? Because the macro also provides the testcontext as the first argument.
export interface<T, I, E>
Same for Macros
// @ivogabe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated with T as first type param
Sorry for the late reply guys. Only have one comment. If that's answered/addressed, seems good to merge. |
types/base.d.ts
Outdated
|
||
export interface Macro<T, I, E> { | ||
(t: T, input: I, expected: E): void; | ||
title? (providedTitle: string, input: I, expected: E): string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Last small remark. Should we call this title: string
instead of providedTitle: string
? Autocompletion tools show the param names and title: string
might be better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renaming it to title
might be confusing, since you'd then have a function and an argument both named title
. I did a quick test and autocompletion doesn't show the argument name when implementing it like this:
export interface Macro<T, I, E> {
(t: T, input: I, expected: E): void;
title? (providedTitle: string, input: I, expected: E): string;
}
const macro: Macro<...> = () => {};
macro.title = (/* cursor */) => "";
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we call this
title: string
instead ofprovidedTitle: string
?
I would agree with @ivogabe I would lean against renaming, it could be confusing. Additionally providedTitle
is how this is documented in the readme.
I did a quick test and autocompletion doesn't show the argument name when implementing it like this
I've noticed autocomplete is not suggesting as well, though the type checker is validating correctly.
I'm open to suggestions on how to improve the typing to get autocompletion here.
types/base.d.ts
Outdated
export interface Macro<T, I, E> { | ||
(t: T, input: I, expected: E): void; | ||
title? (providedTitle: string, input: I, expected: E): string; | ||
(t: T, input?: I, expected?: E): void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should not be marked optional here, since that would require a user to make them optional as well. Making them optional in test
and the generator script is sufficient. (A function with less arguments is assignable to a function type with more arguments.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, updated
Is this good to merge now? |
I'm not entirely sure to be honest. It doesn't feel 100% right to me as a macro could actually be anything. This implementation was based upon the example in the readme. function macro(t, input, expected) {
t.is(eval(input), expected);
}
test('2 + 2 === 4', macro, '2 + 2', 4);
test('2 * 3 === 6', macro, '2 * 3', 6); But what if I want to do this function macro(t, obj) {
for (const key of Object.keys(obj)) {
t.is(eval(obj[key]), key);
}
}
test('2 + 2 === 4', macro, {4: '2 + 2'});
test('2 * 3 === 6', macro, {6: '2 * 3'}); This doesn't follow the export function test<I, E> (name: string, run: Macros<ContextualTestContext>, arg1?: any): void;
export function test<I, E> (name: string, run: Macros<ContextualTestContext>, arg1?: any, arg2?: any): void;
export function test<I, E> (name: string, run: Macros<ContextualTestContext>, arg1?: any, arg2?: any, arg3?: any): void;
export function test<I, E> (name: string, run: Macros<ContextualTestContext>, arg1?: any, arg2?: any, arg3?: any, arg4?: any): void;
export function test<I, E> (name: string, run: Macros<ContextualTestContext>, arg1?: any, arg2?: any, arg3?: any, arg4?: any, arg5?: any): void; The current implementation forces the user to use it like the example provided in the readme. But macros are much more powerful then that. This is just my opinion. If you guys are fine with what we have now, It's ok to merge. Just wanted to bring this up to everyones attention. |
|
@SamVerschueren Then all parameters are still typed as Maybe a bit off-topic, but I think that it might be better to define macros based on partial application/currying like this: (using the same example) const macro = (obj: { [key: number]: string }) => (t: ContextualTestContext) => {
for (const key of Object.keys(obj)) {
t.is(eval(obj[key]), key);
}
}
test('2 + 2 === 4', macro({4: '2 + 2'}));
test('2 * 3 === 6', macro({6: '2 * 3'})); That doesn't require a special overload for macros, and can thus be typed correctly. It might be an idea to type the macro overload with |
Alright, let's go with
👍 Could be added to the TypeScript recipe. |
Updated with |
Thank you @ChristianMurphy for this excellent contribution, and to @ivogabe and @SamVerschueren for reviewing :) |
This brings support for test macros in the typescript.
It adds a macro interface, overloads the test function definition, and overloads all combinations of test modifiers.
Similar to #895 but built on top of #884