Giter VIP home page Giter VIP logo

Comments (3)

ehmicky avatar ehmicky commented on May 29, 2024

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.

jrmdayn avatar jrmdayn commented on May 29, 2024

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.

ehmicky avatar ehmicky commented on May 29, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.