question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Don't require to implement optional abstract properties

See original GitHub issue

Search Terms

abstract optional override

Suggestion

If in abstract class abstract property is marked as optional, allow child classes not to implement it. So I suggest to remove an error for property x of class D in the following code:

https://www.typescriptlang.org/play?ts=4.0.2#code/IYIwzgLgTsDGEAJYBthjAgggg3gKAUIQAcoB7CAU3koBMFRIZ4EpLhayA7ZATwQAeAfgBcCLgFcAtiEpQCRUhWpV6jaHERsO3Pgl5jJMuQgA+CCV1qUAZgEsudPAsLEJIZHdgJOAZSkQABYAFACUuC5EhLDcYGTIlAB0yGQA5sFBdmCJAgA0CJnZvKGRAL545XgoaBgAIgiUAlRWGNj4UUpUNPTanDz8-AC8CACMAAwVzo4A7gi1YYl+ASGhQA

abstract class A {
    protected abstract readonly x?: number
    protected abstract readonly y: number | undefined

    public doSmth() {
        console.log(this.x, this.y)
    }
}

class D extends A {
    protected readonly y = 10
}

new D().doSmth()

But property y still must be implemented.

Note that we already can do some sort of it

https://www.typescriptlang.org/play?ts=4.0.2#code/JYOwLgpgTgZghgYwgAgJLIN4Chm+QBygHt9owBPAYThAGUBrYfAfgC5kQBXAWwCNoceQiTLkAspwDOYVN3wAbCNwjh2XPtGQAfZJxAATCDFAR9g3OeTKwACyL7qdRiwAUASnYA3IsDN4rELb2EtKyCkoqYO5ePmYAvlhYcLzSUIhgyAjycJKSyACCmJbCpFAUjgxMbBw8-FCWyanpBMSlFCEycorKqjUaUNq6BkYmfniNYGkIGSWiHWHdkQAqRETV6nWJ-tZ2DjSVrh7I3r4NKZPNO8FSneE9UUcnY7gTUxlX+vNdEeAra9HHWJYBJYLI5PKUQoQAAekAMeUK2AA9AAqSwAOSIIAAtK9mmDcsgAOSQonIfRECB5EBEDLAb73ZCgGzQYCQfTIPHTAL9YkfL53SJkmDEbiZbKEon5IkAOhcACYAKwARkVbgxWNx5ze4vBxNJ5Mp1NpTIZkSZIBZUDZpk52suSjqfMCuwFi1+q2Fot1kulcqVqvV-kxOK5GQJeRJ0sNVI4JvpgvAFqtNo5YZ5TqJszK4huCx+YC9RDFEeJfoVKrVGtD9u5pajZIpsZpdLNSeZrPZdqa3OUvKzrTmebbYD+RZLEsj5YDVZRSOBiVLlHQCfdYDy6GRaP8lEn+tQZNACCIUCgEGm8nIpsT64tkFgiBQRIPMssS3IpH3h7y3GAuVAADmyC2CgMBEPI8hEAA7oBLQiGUwCxiKxbAR+T4Huw2btMON4ADQBEEnw4WuCoACzygADOqc7AkAA

interface I {
    propertyCanSkip?: number
    propertyMustImplement: number | undefined
    
    methodCanSkip?(): void
    methodMustImplement(): void
}

abstract class A {
    propertyCanSkip?: number
    abstract propertyMustImplement: number | undefined
    abstract propertyMustImplementToo?: number

    methodCanSkip?(): void
    abstract methodMustImplement(): void
    abstract methodMustImplementToo?(): void
}

class CA extends A {
/*
    Non-abstract class 'CA' does not implement inherited abstract member 'methodMustImplement' from class 'A'.(2515)
    Non-abstract class 'CA' does not implement inherited abstract member 'methodMustImplementToo' from class 'A'.(2515)
    Non-abstract class 'CA' does not implement inherited abstract member 'propertyMustImplement' from class 'A'.(2515)
    Non-abstract class 'CA' does not implement inherited abstract member 'propertyMustImplementToo' from class 'A'.(2515)
*/
}

class CI implements I {
/*
    Class 'CI' incorrectly implements interface 'I'.
    Type 'CI' is missing the following properties from type 'I': propertyMustImplement, methodMustImplement(2420)
*/
}

But there is a set of problems for properties

We have two ways to override property (via property declaration or via getter and setter). And now (in TS4) the limitation have changed. For nonabstract property base class always defines how it should be implemented in children.

Let’s look what implementations are possible (don’t forget about useDefineForClassFields compiler flag that makes it more important):

Code Property can be omitted Child can implement as property Child can implement as get/set
propertyCanSkip?: number Yes Yes No
abstract propertyMustImplement: number | undefined No Yes Yes (except https://github.com/microsoft/TypeScript/issues/40632)
abstract propertyMustImplementToo?: number No Yes Yes (except https://github.com/microsoft/TypeScript/issues/40632)
get getter?(): number N/A N/A N/A
abstract get getterMustImplement(): number | undefined No No Yes
abstract getterToo?(): number N/A N/A N/A

It’s easy to see, that if the property should really be optional, there is only one way to make it such which will not allow to implement it as getter and setter. But we have 2 absolutely identical lines with optional and nonoptional abstract property. I see no sense for them to be synonyms as ? in the 3rd line definitely says that the property is optional, but doesn’t give me ability to make so in further code.

So I propose to change this table in following way:

Code Property can be omitted Child can implement as property Child can implement as get/set
propertyCanSkip?: number Yes Yes No
abstract propertyMustImplement: number | undefined No Yes Yes
abstract propertyCanSkipToo?: number Yes Yes Yes
get getter?(): number N/A N/A N/A
abstract get getterMustImplement(): number | undefined No No Yes
abstract get getterCanSkipToo?(): number Yes No Yes

Abstract getter is NOT a part of this feature request, just shown for consistency.

Use Cases

Provide ability to list and use for reading an optional property in abstract class without limiting a way of its implementation in child classes. Such problem occured in a real project because of migration from TS3 to TS4. Before that it was possible, but because of breaking changes of TS4 it’s not anymore.

https://www.typescriptlang.org/play?ts=4.0.2#code/IYIwzgLgTsDGEAJYBthjAgggg3gKAUIQAcoB7CAU3koBMEpLhayA7ZATwTAFsIALAPwAuBKwCuPEJSh4CRYuJDIAlrAQAzABQBKXPKJFYbMGWSUAdMjIBzLQJVgLvAToMIAvni94UaDABCCJQAHlSstBjY+IYu-AgAvAgAjAAM3nJ+6AgAwsFhlBFR+oY2lIhxurgM5eJQrAgAssACFjARZDxVXj5ZGAAi+eGRWPo+rJQA7ggBuhbabhPTOXMLeEsI-au6QA

abstract class A {
    protected readonly smth?: number

    public f() {
        console.log(this.smth)
    }
}

class B extends A {
    smth = 10
}

class C extends A {
    get smth() { return Math.random() }
}

class D extends A {
}

new B().f()
new C().f()
new D().f()

Examples

See above.

Checklist

My suggestion meets these guidelines:

  • This wouldn’t be a breaking change in existing TypeScript/JavaScript code
  • This wouldn’t change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn’t a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript’s Design Goals.

Why it’s not a breaking change?

It changes behavior of existing construction, but it’s not a breaking change in terms of code.

If you had your code working and you have

abstract propertyMustImplementToo?: number

in it, that means that you implemented this property in all child classes. So after its meaning changes all you code keeps being valid and compiles into absolutely the same javascript code as before. Nothing changed.

At the same time, for further development you have to decide whether you want to allow child classes to skip the property or not. If yes, or you don’t care - keep it with ? as it is. If no then update it to

abstract propertyMustImplementToo: number | undefined

without any other changes needed.

Related Issues:

https://github.com/microsoft/TypeScript/issues/6413 https://github.com/microsoft/TypeScript/issues/22939

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:10
  • Comments:13 (7 by maintainers)

github_iconTop GitHub Comments

3reactions
sandersncommented, Sep 25, 2020

I said

abstract class C { p?: number }
abstract class K { abstract p?: number }

are checked the same today (and emitted, but that’s bug #40699)

class D extends C { } // doesn't need to be abstract, p is optional
class B extends K { } // currently: B must be abstract

This issue requests that B be valid code, and not required to be abstract. But that’s the same as D.

1reaction
bogdanbcommented, Nov 1, 2022

I’m having something like the code below (simplified) in our project, which we’d like to run with :

abstract class BaseClass {
    abstract readonly requiredProperty: string;
    abstract readonly optionalProperty?: string;
}

The idea is that sub-classes must specify how the properties are defined (including if they are based on getters or values), and once they do that they can’t be changed, but the optionalProperty either is a string or it’s not present at all. Note that exactOptionalPropertyTypes makes the distinction between “a property is not present” and “a property has the undefined value” explicit.

In apparent contradiction with some of the comments above, it seems impossible to actually define a subclass of BaseClass whose instances don’t have the optionalProperty at all.

(Well, I can define it by adding a constructor that explicitly deletes the property, but that’s not what I mean. TypeScript complains about deleting readonly properties, so I’d need to add some casts.)

Read more comments on GitHub >

github_iconTop Results From Across the Web

Typescript abstract optional method - Stack Overflow
1. I don't think optional abstract methods are supported. · 1. the correct answer is @idsbllp this is a unique feature of typescript...
Read more >
How to define abstract properties - C# Programming Guide
Learn how to define abstract properties in C#. Declaring an abstract property means that a class supports a property.
Read more >
Abstract Classes - Design Patterns in TypeScript - sbcode.net
Abstract classes are like a mixture of implementing interfaces and extending a class in one step. You can create a class with optional...
Read more >
TypeScript Abstract Class - TutorialsTeacher
We cannot create an instance of an abstract class. An abstract class typically includes one or more abstract methods or property declarations.
Read more >
Properties | Kotlin Documentation
To use a property, simply refer to it by its name: ... The initializer, getter, and setter are optional. The property type is...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found