Comments (3)
Hi @jrmdayn,
thanks for this very cool lib!!
Thanks! ❤️
TypeScript is based on structural typing, as opposed to nominal typing. The error classes above are identical from a functionality standpoint, which results in identical types.
This is not specific to this library: TypeScript itself types native error classes like this. For example, you would get the same issue when using RangeError
and URIError
.
A common solution to this problem is to add a tag. For example, the error class' type could have an additional attribute to differentiate it from other classes. For instance, error.name
could be the a literal string like "InputError" as const
instead of being a generic string
. This is actually what this library used to do in previous releases. However, this breaks inheritance: if you create a subclass of InputError
, TypeScript will not consider it a child type anymore because of the mismatch on that tag attribute.
That being said, if you do not use inheritance, you can circumvent this by using internal props
.
import ModernError from 'modern-errors'
export const BaseError = ModernError.subclass('BaseError')
export const UnknownError = BaseError.subclass('UnknownError', { props: { _tag: 'UnknownError' as const } })
export const InputError = BaseError.subclass('InputError', { props: { _tag: 'InputError' as const } })
export const AuthError = BaseError.subclass('AuthError', { props: { _tag: 'AuthError' as const } })
export const DatabaseError = BaseError.subclass('DatabaseError', { props: { _tag: 'DatabaseError' as const } })
Would this solution work in your case?
from modern-errors.
Thanks for your response. I understand the solution you offer. I tried it on TS playground but I run into a type issue so not sure that it would solve my use case:
type E = typeof UnknownError | typeof InputError
const u = new UnknownError('')
export const e: E = u // TS error
From a DX point a vue, the type E
is not super friendly:
type E = ErrorSubclassCore<[], {
_tag: "UnknownError";
}, CustomClass> | ErrorSubclassCore<[], {
_tag: "InputError";
}, CustomClass>
hard to read if I have more than 2 errors IMO.
from modern-errors.
The following should work:
type E = InstanceType<typeof UnknownError> | InstanceType<typeof InputError>
typeof InputError
is the type of the error class, not the error instance.
The reason this might be surprising and counter-intuitive is because you might be used to classes in TypeScript working differently. For example, TypeScript types the native error classes like this: URIError
is the error instance type, while typeof URIError
is the error class type. This is nice and simple. This behavior only happens when using classes:
class ErrorSubclassCore {
constructor(...)
}
Unfortunately, using classes in TypeScript has the following limitation: the instance type cannot use conditionals. On the other hand, classes can be emulated with interfaces, which do not have this limitation:
interface ErrorSubclassCore {
new(...): condition ? valueOne : valueTwo
}
Without getting into the details, there are some additional limitations of using classes (as opposed to interfaces) when it comes to using generics for the constructor, and also in terms of typing the prototype
(which is important for modern-errors
since it is used for instanceof
narrowing).
In a nutshell, the error class is typed using an interface
instead of a class
to use those additional type features. However, it has the following adverse consequence in terms of developer experience: InstanceType<typeof ErrorClass>
must be used instead of ErrorClass
to get the instance type. I am not too happy about this, but I don't quite think there is another way to do this with TypeScript unfortunately :-/
Thanks for pointing out at this issue though. 🙏 I have added some documentation around it in 73ffaf6, which I hope will help other users.
from modern-errors.
Related Issues (9)
- Typescript constructor example? HOT 6
- `errors` property is missing in instance type of custom error HOT 13
- TypeScript issues with `moduleResolution: "nodenext"` and `declaration: true` HOT 19
- Why not to support both ESM and CommonJS? HOT 2
- TypeScript + CommonJS support HOT 6
- support for mandatory props along with type-check support HOT 5
- [Question] ES6 class extensions HOT 4
- SimpleSetProps does not emulate SetProps HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from modern-errors.