Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add error predicate #27

Merged
merged 5 commits into from Dec 2, 2017
Merged

Add error predicate #27

merged 5 commits into from Dec 2, 2017

Conversation

SamVerschueren
Copy link
Collaborator

馃帀 Feedback welcome!

@SamVerschueren SamVerschueren mentioned this pull request Nov 27, 2017
87 tasks
}

/**
* Test an Error to an EvalError.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing be here and in the other descriptions.

/**
* Test an Error to a TypeError.
*/
get typeError() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should put this above evalError as it's the most common Error subclass and should be sorted first of subclasses in autocomplete.

*
* @param instance The expected instance type of the error.
*/
private addErrorTypeValidator(instance: any) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's your strategy for method sorting? I usually put private method/props first as they're usually used by the public ones and therefore it's nice to read their logic first so you know how they work before you encounter calls to them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you're right. Didn't thought about it and refactored some things last minute. Private-first makes sense.

@sindresorhus
Copy link
Owner

How should we handle the Object predicate? For example, here we could also use a instanceOf predicate, but I'm not sure whether this will be inherited from the Object predicate or whether we should manually add it here.

@SamVerschueren
Copy link
Collaborator Author

Not sure if I understand you correctly. So what you want is to add something like ow.error.instanceOf(CustomError) as well? And you're wondering if we should use a base class or not?

It could/would look like this.

abstract class InstanceOfPredicate<T> extends Predicate<T> {
	constructor(type: string, context?: Context) {
		super(type, context);
	}

	instanceOf(instance: any) {
		return this.addValidator({
			message: obj => `Expected \`${obj.name}\` to be an instance of  \`${instance.name}\``,
			validator: obj => obj instanceof instance
		});
	}
}

class ErrorPredicate extends InstanceOfPredicate<Error> {
	constructor(context?: Context) {
		super('error', context);
	}
}

class ObjectPredicate extends InstanceOfPredicate<Object> {
	constructor(context?: Context) {
		super('object', context);
	}
}

Not sure about this. It deduplicates code, but it's only a small piece and it doesn't allow you to specify a custom method description. Something like Test if an Error is of a specific type.. It would be more generic. What's your feeling about this?

@sindresorhus
Copy link
Owner

sindresorhus commented Nov 28, 2017

No, I meant that errors are objects, so it might make sense for errors to inherit the object predicates, but thinking about this a bit more I don't think it makes sense as the error message would be wrong and not all object predicates makes sense for error.

Let's just add the instanceOf method directly. Maybe we could name it something better. Actually, maybe we should have two methods. One to test that an error is an error type and one to test if it's a descendent (instanceOf) from an error type. Or am I overthinking it?

@sindresorhus
Copy link
Owner

sindresorhus commented Nov 28, 2017

It's common to have custom properties on errors, so maybe we should add a hasKeys method too?

@SamVerschueren
Copy link
Collaborator Author

I took a look for instanceOf but it doesn't really work. This is the result in ES5.

class CustomError extends Error {
	constructor(message: string) {
		super(message);
		this.name = 'CustomError';
	}
}

const err = new CustomError('foo');
console.log(err instanceof CustomError);  // false
console.log(err instanceof Error);  // true

This works in ES2015 though, but apparently this is very difficult to transpile in ES5 code. So currently, not sure if we should add an instanceOf method because it doesn't add anything extra over the already built-in ones like .typeError, .uriError, etc.?

hasKeys(...keys: string[]) {
return this.addValidator({
message: () => `Expected error message to have keys \`${keys.join('`, `')}\``,
validator: error => keys.every(key => (error as Object).hasOwnProperty(key))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to use as Object? Errors are objects.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you're right. I thought I had to do this at first because I didn't get any intellisense without the casting. But it works fine without as well.

@sindresorhus
Copy link
Owner

This is just one of many issues we have to deal with because we compile to ES5. I think we instead should compile to ES6 and use Babel to transpile the remaining things to get Node.js 4 compat. What do you think?

@sindresorhus
Copy link
Owner

I was actually thinking:

class CustomError extends Error {
	constructor(message: string) {
		super(message);
		this.name = 'CustomError';
	}
}

const isType = (err, constructor) => Object.getPrototypeOf(err) === constructor.prototype;

console.log(isType(new CustomError('x'), CustomError));

So not instance of, but rather testing the type directly. Or is it better to just use instanceof?

@sindresorhus
Copy link
Owner

because it doesn't add anything extra over the already built-in ones like .typeError, .uriError, etc.?

It would let us check if something is a custom Error.

@SamVerschueren
Copy link
Collaborator Author

It would let us check if something is a custom Error.

Yes I totally agree, if it works correctly :) which it doesn't with instanceof in ES5.

The Object.getPrototypeOf(err) === constructor.prototype check doesn't work either. Here's a more official explanation https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work. Using the setPrototypeOf method gives the correct results, event with instanceof.

I think we instead should compile to ES6 and use Babel to transpile the remaining things to get Node.js 4 compat. What do you think?

If we go down this road, we might reconsider support for Node.js 4 as it gets end of life in April 2018. Not sure how easy it is to set this up though, never really used Babel a lot.

@sindresorhus
Copy link
Owner

The Object.getPrototypeOf(err) === constructor.prototype check doesn't work either. Here's a more official explanation Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work. Using the setPrototypeOf method gives the correct results, event with instanceof.

Sorry, I should have been clearer. I know that's not working right now either. I just meant to ask if that's how we should do it if we fix the inheritance problem.

If we go down this road, we might reconsider support for Node.js 4 as it gets end of life in April 2018. Not sure how easy it is to set this up though, never really used Babel a lot.

Yeah, maybe we should just target Node.js 6 and we don't have to use Babel at all. I think we should.

@sindresorhus
Copy link
Owner

Yeah, maybe we should just target Node.js 6 and we don't have to use Babel at all. I think we should.

Done: 474139f

@sindresorhus
Copy link
Owner

sindresorhus commented Dec 1, 2017

Do you think we should have a check for any inheritance (instanceof) or direct inheritance?

@SamVerschueren
Copy link
Collaborator Author

Do you think we should have a check for any inheritance (instanceof) or direct inheritance?

Not really sure to be honest. If we name the method instanceOf, it might be weird that ow(new CustomError('foo'), ow.error.instanceOf(Error)) returns falls. So I guess using instanceof makes sense here. What's your feeling?

@SamVerschueren
Copy link
Collaborator Author

I removed the private method and re-used instanceOf in the typeError, rangeError, ... checks.

@sindresorhus sindresorhus merged commit f8b3191 into master Dec 2, 2017
@sindresorhus sindresorhus deleted the error-predicate branch December 2, 2017 10:46
@sindresorhus
Copy link
Owner

馃檶

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants