Skip to content

Conversation

mrgrain
Copy link
Contributor

@mrgrain mrgrain commented Sep 11, 2025

Relaxes the the restriction on covariant overrides as currently documented here:
https://aws.github.io/jsii/user-guides/lib-author/typescript-restrictions/#covariant-overrides-parameter-list-changes

Use of this new feature is indicated by the class-covariant-overrides feature in the produced jsii assembly.

What is the change?

It is now allowed to override the type of readonly class properties and the return type of class methods in class inheritance, as long as the changes are covariant to the superclass. Importantly, we still don't allow covariant overrides when implementing interfaces.

✅ now possible

export class Superclass {}
export class Subclass extends Superclass {}

export class SomethingUnspecific {
  public readonly something = new Superclass();
  public returnSomething(): Superclass {
    return new Superclass();
  }
}

// Subclass is covariant to Superclass
export class SomethingSpecific extends SomethingUnspecific {
  public readonly something = new Subclass();
  public returnSomething(): Subclass {
    return new Subclass();
  }
}

❌ still prohibited

export class Superclass {}
export class Subclass extends Superclass {}

export interface ISomething {
  something: Superclass;
  returnSomething(): Superclass;
}

// ❌ covariant changes are still prohibited when implementing an interface
export class SomethingImpl implements ISomething {
  public something: Subclass = new Subclass();
  public returnSomething(): Subclass {
    return new Subclass();
  }
}

Why is this safe now?

We can make these changes now, because C# 9 has added the necessary support, and this version of C# has been introduced in .NET 5.
In jsii we have now made the necessary change to increase the target framework to net6.0.

C# references:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/covariant-returns
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/override
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/knowing-when-to-use-override-and-new-keywords


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@mrgrain mrgrain changed the title feat: allow covariant-overrides feat: allow some covariant overrides Sep 11, 2025
@mrgrain mrgrain force-pushed the mrgrain/feat/covariant-overrides branch 4 times, most recently from df6761e to 83e56ae Compare September 12, 2025 09:57
@mrgrain mrgrain changed the title feat: allow some covariant overrides feat: allow covariant overrides in classes Sep 12, 2025
@mrgrain mrgrain force-pushed the mrgrain/feat/covariant-overrides branch 2 times, most recently from 079dca9 to bd3a0e0 Compare September 12, 2025 10:23
@mrgrain mrgrain force-pushed the mrgrain/feat/covariant-overrides branch from bd3a0e0 to a419c49 Compare September 12, 2025 11:30
label: string,
action: string,
opts: {
allowCovariance: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

Return type covariance.

By the way, does that mean we also have argument type contravariance? Or no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's only return types and readonly properties. Argument type covariance is also still not allowed.

return true;
}

if (!spec.isNamedTypeReference(candidateType) || !spec.isNamedTypeReference(expectedType)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

For collections't, won't A ⊑ B ⇒ List<A> ⊑ List<B> ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've currently excluded lists. Will see if that's supported in C#.

Comment on lines +415 to +417
// Handle class-to-class inheritance
if (spec.isClassType(candidateTypeSpec) && spec.isClassType(expectedTypeSpec)) {
// Check if candidateType extends expectedType (directly or indirectly)
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we also call _interfaceExtendsInterface here?

Co-authored-by: Rico Hermans <rix0rrr@gmail.com>
Signed-off-by: Momo Kornher <mail@moritzkornher.de>
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.

2 participants