Allow interfaces to declare members that inheritors must define #56774
Labels
Awaiting More Feedback
This means we'd like to hear from more people who would be helped by this feature
Suggestion
An idea for TypeScript
π Search Terms
interface enforce, interface abstract, interface inherit
β Viability Checklist
β Suggestion
Allowing interfaces, especially those exported by libraries, to specify mandatory, user-defined type-narrowing would make many situations simpler, removing some of the hoops that developers jump through to defer type resolution.
This might look something like:
For a more concrete example, we might revisit the
Shape
example from the TS docs:This is nice, but it runs into a problem - Circle and Square can't share any properties because we've removed inheritance from the equation, so area and perimeter would need to be defined separately on each or defined on a shared, extended interface like
BaseShape
. Instead, we can stick with the original inheritance patterns and enforce properties further down the inheritance chain.When it comes time to consume this via a function, we can specify if we need a
Shape
orShapeWithKind
or maybe some shapes have afamily
property, extendingShape
,WithKind
, andWithFamily
. This is somewhat reminiscent of Dart'sabstract class mixin
s but exclusively looks at the implications on the type system, disregarding the entirety of the runtime impact of that inspiration.π Motivating Example
Abstract classes let you add base behavior so programmers don't have to code everything, while still forcing them to follow your design. The new interface properties allow the same within the type system, improving the developer experience for consumers of your module as well as allowing more robust mechanisms to defer type resolution to the point of use rather than the point of definition.
π» Use Cases
What do you want to use this for?
I want to use this to enforce the shape of an interface without providing all of the type information at the point of use. A colleague wants to effectively write
interface Foo {...} satisfies Bar
, which would be possible withinterface Foo extends AbstractFoo {...}
. If paired with Add anabsent
type forbidding presenceΒ #55143 (or even just judicious use of thenever
bottom type) you could apply full constraints to inheriting interfaces in terms of what members must be defined and what members must not be, which seems especially powerful for overload resolution.What shortcomings exist with current approaches?
The only way that I know of to do something similar is to use a dummy utility type, which then means you're either separating errors from interface signatures or defining everything as a type alias and losing the value of interfaces.
What workarounds are you using in the meantime?
The text was updated successfully, but these errors were encountered: