From 3fe512a6bdab19a2d39efeb6f63a8ad9d345a70e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 25 Dec 2024 14:57:47 +0100 Subject: [PATCH 1/3] Keep accessors as accessors in emitted anonymous class declarations --- src/compiler/checker.ts | 65 +++++++++++----- ...anonymousClassAccessorsDeclarationEmit1.js | 66 ++++++++++++++++ ...mousClassAccessorsDeclarationEmit1.symbols | 53 +++++++++++++ ...nymousClassAccessorsDeclarationEmit1.types | 75 +++++++++++++++++++ tests/baselines/reference/autoAccessor8.js | 6 +- .../jsDeclarationsFunctionLikeClasses2.types | 32 ++++---- ...anonymousClassAccessorsDeclarationEmit1.ts | 25 +++++++ 7 files changed, 284 insertions(+), 38 deletions(-) create mode 100644 tests/baselines/reference/anonymousClassAccessorsDeclarationEmit1.js create mode 100644 tests/baselines/reference/anonymousClassAccessorsDeclarationEmit1.symbols create mode 100644 tests/baselines/reference/anonymousClassAccessorsDeclarationEmit1.types create mode 100644 tests/cases/conformance/declarationEmit/anonymousClassAccessorsDeclarationEmit1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 80b61edd3657a..04b8491c6aa92 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -465,6 +465,7 @@ import { IntroducesNewScopeNode, isAccessExpression, isAccessor, + isAccessorModifier, isAliasableExpression, isAmbientModule, isArray, @@ -7412,26 +7413,50 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (propertySymbol.flags & SymbolFlags.Accessor) { const writeType = getWriteTypeOfSymbol(propertySymbol); - if (propertyType !== writeType && !isErrorType(propertyType) && !isErrorType(writeType)) { - const getterDeclaration = getDeclarationOfKind(propertySymbol, SyntaxKind.GetAccessor)!; - const getterSignature = getSignatureFromDeclaration(getterDeclaration); - typeElements.push( - setCommentRange( - context, - signatureToSignatureDeclarationHelper(getterSignature, SyntaxKind.GetAccessor, context, { name: propertyName }) as GetAccessorDeclaration, - getterDeclaration, - ), - ); - const setterDeclaration = getDeclarationOfKind(propertySymbol, SyntaxKind.SetAccessor)!; - const setterSignature = getSignatureFromDeclaration(setterDeclaration); - typeElements.push( - setCommentRange( - context, - signatureToSignatureDeclarationHelper(setterSignature, SyntaxKind.SetAccessor, context, { name: propertyName }) as SetAccessorDeclaration, - setterDeclaration, - ), - ); - return; + if (!isErrorType(propertyType) && !isErrorType(writeType)) { + const propDeclaration = getDeclarationOfKind(propertySymbol, SyntaxKind.PropertyDeclaration); + if (propertyType !== writeType || propertySymbol.parent!.flags & SymbolFlags.Class && !propDeclaration) { + const getterDeclaration = getDeclarationOfKind(propertySymbol, SyntaxKind.GetAccessor); + if (getterDeclaration) { + const getterSignature = getSignatureFromDeclaration(getterDeclaration); + typeElements.push( + setCommentRange( + context, + signatureToSignatureDeclarationHelper(getterSignature, SyntaxKind.GetAccessor, context, { name: propertyName }) as GetAccessorDeclaration, + getterDeclaration, + ), + ); + } + const setterDeclaration = getDeclarationOfKind(propertySymbol, SyntaxKind.SetAccessor); + if (setterDeclaration) { + const setterSignature = getSignatureFromDeclaration(setterDeclaration); + typeElements.push( + setCommentRange( + context, + signatureToSignatureDeclarationHelper(setterSignature, SyntaxKind.SetAccessor, context, { name: propertyName }) as SetAccessorDeclaration, + setterDeclaration, + ), + ); + } + return; + } + if (propertySymbol.parent!.flags & SymbolFlags.Class && propDeclaration && find(propDeclaration.modifiers, isAccessorModifier)) { + const fakeGetterSignature = createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, emptyArray, propertyType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); + typeElements.push( + setCommentRange( + context, + signatureToSignatureDeclarationHelper(fakeGetterSignature, SyntaxKind.GetAccessor, context, { name: propertyName }) as GetAccessorDeclaration, + propDeclaration, + ), + ); + const setterParam = createSymbol(SymbolFlags.FunctionScopedVariable, "arg" as __String); + setterParam.links.type = writeType; + const fakeSetterSignature = createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, [setterParam], voidType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); + typeElements.push( + signatureToSignatureDeclarationHelper(fakeSetterSignature, SyntaxKind.SetAccessor, context, { name: propertyName }) as SetAccessorDeclaration, + ); + return; + } } } diff --git a/tests/baselines/reference/anonymousClassAccessorsDeclarationEmit1.js b/tests/baselines/reference/anonymousClassAccessorsDeclarationEmit1.js new file mode 100644 index 0000000000000..97be26cb3b8df --- /dev/null +++ b/tests/baselines/reference/anonymousClassAccessorsDeclarationEmit1.js @@ -0,0 +1,66 @@ +//// [tests/cases/conformance/declarationEmit/anonymousClassAccessorsDeclarationEmit1.ts] //// + +//// [anonymousClassAccessorsDeclarationEmit1.ts] +export abstract class Base { + accessor a = 1; +} + +export function middle(Super = Base) { + abstract class Middle extends Super {} + return Middle; +} + +class A { + constructor(...args: any[]) {} +} + +export function Mixin(Super: T) { + return class B extends Super { + get myName(): string { + return "B"; + } + set myName(arg: string) {} + }; +} + + +//// [anonymousClassAccessorsDeclarationEmit1.js] +export class Base { + accessor a = 1; +} +export function middle(Super = Base) { + class Middle extends Super { + } + return Middle; +} +class A { + constructor(...args) { } +} +export function Mixin(Super) { + return class B extends Super { + get myName() { + return "B"; + } + set myName(arg) { } + }; +} + + +//// [anonymousClassAccessorsDeclarationEmit1.d.ts] +export declare abstract class Base { + accessor a: number; +} +export declare function middle(Super?: typeof Base): abstract new () => { + get a(): number; + set a(arg: number); +}; +declare class A { + constructor(...args: any[]); +} +export declare function Mixin(Super: T): { + new (...args: any[]): { + get myName(): string; + set myName(arg: string); + }; +} & T; +export {}; diff --git a/tests/baselines/reference/anonymousClassAccessorsDeclarationEmit1.symbols b/tests/baselines/reference/anonymousClassAccessorsDeclarationEmit1.symbols new file mode 100644 index 0000000000000..de616f3a700e1 --- /dev/null +++ b/tests/baselines/reference/anonymousClassAccessorsDeclarationEmit1.symbols @@ -0,0 +1,53 @@ +//// [tests/cases/conformance/declarationEmit/anonymousClassAccessorsDeclarationEmit1.ts] //// + +=== anonymousClassAccessorsDeclarationEmit1.ts === +export abstract class Base { +>Base : Symbol(Base, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 0, 0)) + + accessor a = 1; +>a : Symbol(Base.a, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 0, 28)) +} + +export function middle(Super = Base) { +>middle : Symbol(middle, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 2, 1)) +>Super : Symbol(Super, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 4, 23)) +>Base : Symbol(Base, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 0, 0)) + + abstract class Middle extends Super {} +>Middle : Symbol(Middle, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 4, 38)) +>Super : Symbol(Super, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 4, 23)) + + return Middle; +>Middle : Symbol(Middle, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 4, 38)) +} + +class A { +>A : Symbol(A, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 7, 1)) + + constructor(...args: any[]) {} +>args : Symbol(args, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 10, 14)) +} + +export function Mixin(Super: T) { +>Mixin : Symbol(Mixin, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 11, 1)) +>T : Symbol(T, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 13, 22)) +>A : Symbol(A, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 7, 1)) +>Super : Symbol(Super, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 13, 42)) +>T : Symbol(T, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 13, 22)) + + return class B extends Super { +>B : Symbol(B, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 14, 8)) +>Super : Symbol(Super, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 13, 42)) + + get myName(): string { +>myName : Symbol(B.myName, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 14, 32), Decl(anonymousClassAccessorsDeclarationEmit1.ts, 17, 5)) + + return "B"; + } + set myName(arg: string) {} +>myName : Symbol(B.myName, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 14, 32), Decl(anonymousClassAccessorsDeclarationEmit1.ts, 17, 5)) +>arg : Symbol(arg, Decl(anonymousClassAccessorsDeclarationEmit1.ts, 18, 15)) + + }; +} + diff --git a/tests/baselines/reference/anonymousClassAccessorsDeclarationEmit1.types b/tests/baselines/reference/anonymousClassAccessorsDeclarationEmit1.types new file mode 100644 index 0000000000000..2b07dcaff2698 --- /dev/null +++ b/tests/baselines/reference/anonymousClassAccessorsDeclarationEmit1.types @@ -0,0 +1,75 @@ +//// [tests/cases/conformance/declarationEmit/anonymousClassAccessorsDeclarationEmit1.ts] //// + +=== anonymousClassAccessorsDeclarationEmit1.ts === +export abstract class Base { +>Base : Base +> : ^^^^ + + accessor a = 1; +>a : number +> : ^^^^^^ +>1 : 1 +> : ^ +} + +export function middle(Super = Base) { +>middle : (Super?: typeof Base) => typeof Middle +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Super : typeof Base +> : ^^^^^^^^^^^ +>Base : typeof Base +> : ^^^^^^^^^^^ + + abstract class Middle extends Super {} +>Middle : Middle +> : ^^^^^^ +>Super : Base +> : ^^^^ + + return Middle; +>Middle : typeof Middle +> : ^^^^^^^^^^^^^ +} + +class A { +>A : A +> : ^ + + constructor(...args: any[]) {} +>args : any[] +> : ^^^^^ +} + +export function Mixin(Super: T) { +>Mixin : (Super: T) => { new (...args: any[]): B; prototype: Mixin.B; } & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>A : typeof A +> : ^^^^^^^^ +>Super : T +> : ^ + + return class B extends Super { +>class B extends Super { get myName(): string { return "B"; } set myName(arg: string) {} } : { new (...args: any[]): B; prototype: Mixin.B; } & T +> : ^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>B : { new (...args: any[]): B; prototype: Mixin.B; } & T +> : ^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Super : A +> : ^ + + get myName(): string { +>myName : string +> : ^^^^^^ + + return "B"; +>"B" : "B" +> : ^^^ + } + set myName(arg: string) {} +>myName : string +> : ^^^^^^ +>arg : string +> : ^^^^^^ + + }; +} + diff --git a/tests/baselines/reference/autoAccessor8.js b/tests/baselines/reference/autoAccessor8.js index cb2fb8c393d0f..3203876cec0ac 100644 --- a/tests/baselines/reference/autoAccessor8.js +++ b/tests/baselines/reference/autoAccessor8.js @@ -45,7 +45,9 @@ declare class C2 { } declare function f(): { new (): { - a: any; + get a(): any; + set a(arg: any); }; - b: any; + get b(): any; + set b(arg: any); }; diff --git a/tests/baselines/reference/jsDeclarationsFunctionLikeClasses2.types b/tests/baselines/reference/jsDeclarationsFunctionLikeClasses2.types index 83efd4c430e3f..983f02834dc46 100644 --- a/tests/baselines/reference/jsDeclarationsFunctionLikeClasses2.types +++ b/tests/baselines/reference/jsDeclarationsFunctionLikeClasses2.types @@ -291,16 +291,16 @@ export function Point2D(x, y) { } Point2D.prototype = { ->Point2D.prototype = { __proto__: Vec, get x() { return this.storage[0]; }, /** * @param {number} x */ set x(x) { this.storage[0] = x; }, get y() { return this.storage[1]; }, /** * @param {number} y */ set y(y) { this.storage[1] = y; }} : { __proto__: typeof Vec; x: number; y: number; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^ ->Point2D.prototype : { __proto__: typeof Vec; x: number; y: number; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^ +>Point2D.prototype = { __proto__: Vec, get x() { return this.storage[0]; }, /** * @param {number} x */ set x(x) { this.storage[0] = x; }, get y() { return this.storage[1]; }, /** * @param {number} y */ set y(y) { this.storage[1] = y; }} : { __proto__: typeof Vec; get x(): number; set x(x: number); get y(): number; set y(y: number); } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^ +>Point2D.prototype : { __proto__: typeof Vec; get x(): number; set x(x: number); get y(): number; set y(y: number); } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^ >Point2D : typeof Point2D > : ^^^^^^^^^^^^^^ ->prototype : { __proto__: typeof Vec; x: number; y: number; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^ ->{ __proto__: Vec, get x() { return this.storage[0]; }, /** * @param {number} x */ set x(x) { this.storage[0] = x; }, get y() { return this.storage[1]; }, /** * @param {number} y */ set y(y) { this.storage[1] = y; }} : { __proto__: typeof Vec; x: number; y: number; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^ +>prototype : { __proto__: typeof Vec; get x(): number; set x(x: number); get y(): number; set y(y: number); } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^ +>{ __proto__: Vec, get x() { return this.storage[0]; }, /** * @param {number} x */ set x(x) { this.storage[0] = x; }, get y() { return this.storage[1]; }, /** * @param {number} y */ set y(y) { this.storage[1] = y; }} : { __proto__: typeof Vec; get x(): number; set x(x: number); get y(): number; set y(y: number); } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^ __proto__: Vec, >__proto__ : typeof Vec @@ -315,8 +315,8 @@ Point2D.prototype = { return this.storage[0]; >this.storage[0] : any >this.storage : any ->this : { __proto__: typeof Vec; x: number; y: number; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^ +>this : { __proto__: typeof Vec; get x(): number; set x(x: number); get y(): number; set y(y: number); } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^ >storage : any > : ^^^ >0 : 0 @@ -337,8 +337,8 @@ Point2D.prototype = { > : ^^^^^^ >this.storage[0] : any >this.storage : any ->this : { __proto__: typeof Vec; x: number; y: number; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^ +>this : { __proto__: typeof Vec; get x(): number; set x(x: number); get y(): number; set y(y: number); } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^ >storage : any > : ^^^ >0 : 0 @@ -354,8 +354,8 @@ Point2D.prototype = { return this.storage[1]; >this.storage[1] : any >this.storage : any ->this : { __proto__: typeof Vec; x: number; y: number; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^ +>this : { __proto__: typeof Vec; get x(): number; set x(x: number); get y(): number; set y(y: number); } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^ >storage : any > : ^^^ >1 : 1 @@ -376,8 +376,8 @@ Point2D.prototype = { > : ^^^^^^ >this.storage[1] : any >this.storage : any ->this : { __proto__: typeof Vec; x: number; y: number; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^ +>this : { __proto__: typeof Vec; get x(): number; set x(x: number); get y(): number; set y(y: number); } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^ >storage : any > : ^^^ >1 : 1 diff --git a/tests/cases/conformance/declarationEmit/anonymousClassAccessorsDeclarationEmit1.ts b/tests/cases/conformance/declarationEmit/anonymousClassAccessorsDeclarationEmit1.ts new file mode 100644 index 0000000000000..35f629d3d348c --- /dev/null +++ b/tests/cases/conformance/declarationEmit/anonymousClassAccessorsDeclarationEmit1.ts @@ -0,0 +1,25 @@ +// @strict: true +// @target: esnext +// @declaration: true + +export abstract class Base { + accessor a = 1; +} + +export function middle(Super = Base) { + abstract class Middle extends Super {} + return Middle; +} + +class A { + constructor(...args: any[]) {} +} + +export function Mixin(Super: T) { + return class B extends Super { + get myName(): string { + return "B"; + } + set myName(arg: string) {} + }; +} From 1baa82820c898eb270d4036864cc3b3cd6dfc21d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 1 Jan 2025 20:02:03 +0100 Subject: [PATCH 2/3] Propagate accessor flags in intersected properties --- src/compiler/checker.ts | 10 ++- tests/baselines/reference/mixinAccessors1.js | 66 ++++++++++++++ .../reference/mixinAccessors1.symbols | 56 ++++++++++++ .../baselines/reference/mixinAccessors1.types | 87 +++++++++++++++++++ tests/baselines/reference/mixinAccessors2.js | 54 ++++++++++++ .../reference/mixinAccessors2.symbols | 35 ++++++++ .../baselines/reference/mixinAccessors2.types | 54 ++++++++++++ .../reference/mixinAccessors3.errors.txt | 25 ++++++ tests/baselines/reference/mixinAccessors3.js | 62 +++++++++++++ .../reference/mixinAccessors3.symbols | 42 +++++++++ .../baselines/reference/mixinAccessors3.types | 60 +++++++++++++ tests/baselines/reference/mixinAccessors4.js | 68 +++++++++++++++ .../reference/mixinAccessors4.symbols | 49 +++++++++++ .../baselines/reference/mixinAccessors4.types | 70 +++++++++++++++ .../conformance/classes/mixinAccessors1.ts | 26 ++++++ .../conformance/classes/mixinAccessors2.ts | 17 ++++ .../conformance/classes/mixinAccessors3.ts | 22 +++++ .../conformance/classes/mixinAccessors4.ts | 26 ++++++ 18 files changed, 828 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/mixinAccessors1.js create mode 100644 tests/baselines/reference/mixinAccessors1.symbols create mode 100644 tests/baselines/reference/mixinAccessors1.types create mode 100644 tests/baselines/reference/mixinAccessors2.js create mode 100644 tests/baselines/reference/mixinAccessors2.symbols create mode 100644 tests/baselines/reference/mixinAccessors2.types create mode 100644 tests/baselines/reference/mixinAccessors3.errors.txt create mode 100644 tests/baselines/reference/mixinAccessors3.js create mode 100644 tests/baselines/reference/mixinAccessors3.symbols create mode 100644 tests/baselines/reference/mixinAccessors3.types create mode 100644 tests/baselines/reference/mixinAccessors4.js create mode 100644 tests/baselines/reference/mixinAccessors4.symbols create mode 100644 tests/baselines/reference/mixinAccessors4.types create mode 100644 tests/cases/conformance/classes/mixinAccessors1.ts create mode 100644 tests/cases/conformance/classes/mixinAccessors2.ts create mode 100644 tests/cases/conformance/classes/mixinAccessors3.ts create mode 100644 tests/cases/conformance/classes/mixinAccessors4.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 04b8491c6aa92..8983bdd8b9002 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14938,6 +14938,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined { + let propFlags = SymbolFlags.None; let singleProp: Symbol | undefined; let propSet: Map | undefined; let indexTypes: Type[] | undefined; @@ -14964,6 +14965,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (!singleProp) { singleProp = prop; + propFlags = (prop.flags & SymbolFlags.Accessor) || SymbolFlags.Property; } else if (prop !== singleProp) { const isInstantiation = (getTargetSymbol(prop) || prop) === (getTargetSymbol(singleProp) || singleProp); @@ -14986,6 +14988,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { propSet.set(id, prop); } } + // classes created by mixins are represented as intersections and overriding a property in a derived class redefines it completely at runtime + // so a get accessor can't be merged with a set accessor in a base class, for that reason the accessor flags are only used when they are the same in all consistuents + if (propFlags & SymbolFlags.Accessor && (prop.flags & SymbolFlags.Accessor) !== (propFlags & SymbolFlags.Accessor)) { + propFlags = (propFlags & ~SymbolFlags.Accessor) | SymbolFlags.Property; + } } if (isUnion && isReadonlySymbol(prop)) { checkFlags |= CheckFlags.Readonly; @@ -15004,6 +15011,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { else if (isUnion) { const indexInfo = !isLateBoundName(name) && getApplicableIndexInfoForName(type, name); if (indexInfo) { + propFlags = (propFlags & ~SymbolFlags.Accessor) | SymbolFlags.Property; checkFlags |= CheckFlags.WritePartial | (indexInfo.isReadonly ? CheckFlags.Readonly : 0); indexTypes = append(indexTypes, isTupleType(type) ? getRestTypeOfTupleType(type) || undefinedType : indexInfo.type); } @@ -15082,7 +15090,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { propTypes.push(type); } addRange(propTypes, indexTypes); - const result = createSymbol(SymbolFlags.Property | (optionalFlag ?? 0), name, syntheticFlag | checkFlags); + const result = createSymbol(propFlags | (optionalFlag ?? 0), name, syntheticFlag | checkFlags); result.links.containingType = containingType; if (!hasNonUniformValueDeclaration && firstValueDeclaration) { result.valueDeclaration = firstValueDeclaration; diff --git a/tests/baselines/reference/mixinAccessors1.js b/tests/baselines/reference/mixinAccessors1.js new file mode 100644 index 0000000000000..efbf7c2c43e72 --- /dev/null +++ b/tests/baselines/reference/mixinAccessors1.js @@ -0,0 +1,66 @@ +//// [tests/cases/conformance/classes/mixinAccessors1.ts] //// + +//// [mixinAccessors1.ts] +// https://github.com/microsoft/TypeScript/issues/58790 + +function mixin(superclass: T) { + return class extends superclass { + get validationTarget(): HTMLElement { + return document.createElement("input"); + } + }; +} + +class BaseClass { + get validationTarget(): HTMLElement { + return document.createElement("div"); + } +} + +class MyClass extends mixin(BaseClass) { + get validationTarget(): HTMLElement { + return document.createElement("select"); + } +} + +//// [mixinAccessors1.js] +"use strict"; +// https://github.com/microsoft/TypeScript/issues/58790 +function mixin(superclass) { + return class extends superclass { + get validationTarget() { + return document.createElement("input"); + } + }; +} +class BaseClass { + get validationTarget() { + return document.createElement("div"); + } +} +class MyClass extends mixin(BaseClass) { + get validationTarget() { + return document.createElement("select"); + } +} + + +//// [mixinAccessors1.d.ts] +declare function mixin(superclass: T): { + new (...args: any[]): { + get validationTarget(): HTMLElement; + }; +} & T; +declare class BaseClass { + get validationTarget(): HTMLElement; +} +declare const MyClass_base: { + new (...args: any[]): { + get validationTarget(): HTMLElement; + }; +} & typeof BaseClass; +declare class MyClass extends MyClass_base { + get validationTarget(): HTMLElement; +} diff --git a/tests/baselines/reference/mixinAccessors1.symbols b/tests/baselines/reference/mixinAccessors1.symbols new file mode 100644 index 0000000000000..4fff25e617d4d --- /dev/null +++ b/tests/baselines/reference/mixinAccessors1.symbols @@ -0,0 +1,56 @@ +//// [tests/cases/conformance/classes/mixinAccessors1.ts] //// + +=== mixinAccessors1.ts === +// https://github.com/microsoft/TypeScript/issues/58790 + +function mixin(superclass: T) { +>mixin : Symbol(mixin, Decl(mixinAccessors1.ts, 0, 0)) +>T : Symbol(T, Decl(mixinAccessors1.ts, 2, 15)) +>args : Symbol(args, Decl(mixinAccessors1.ts, 2, 32)) +>superclass : Symbol(superclass, Decl(mixinAccessors1.ts, 2, 55)) +>T : Symbol(T, Decl(mixinAccessors1.ts, 2, 15)) + + return class extends superclass { +>superclass : Symbol(superclass, Decl(mixinAccessors1.ts, 2, 55)) + + get validationTarget(): HTMLElement { +>validationTarget : Symbol((Anonymous class).validationTarget, Decl(mixinAccessors1.ts, 3, 35)) +>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + + return document.createElement("input"); +>document.createElement : Symbol(Document.createElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>document : Symbol(document, Decl(lib.dom.d.ts, --, --)) +>createElement : Symbol(Document.createElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + } + }; +} + +class BaseClass { +>BaseClass : Symbol(BaseClass, Decl(mixinAccessors1.ts, 8, 1)) + + get validationTarget(): HTMLElement { +>validationTarget : Symbol(BaseClass.validationTarget, Decl(mixinAccessors1.ts, 10, 17)) +>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + + return document.createElement("div"); +>document.createElement : Symbol(Document.createElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>document : Symbol(document, Decl(lib.dom.d.ts, --, --)) +>createElement : Symbol(Document.createElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + } +} + +class MyClass extends mixin(BaseClass) { +>MyClass : Symbol(MyClass, Decl(mixinAccessors1.ts, 14, 1)) +>mixin : Symbol(mixin, Decl(mixinAccessors1.ts, 0, 0)) +>BaseClass : Symbol(BaseClass, Decl(mixinAccessors1.ts, 8, 1)) + + get validationTarget(): HTMLElement { +>validationTarget : Symbol(MyClass.validationTarget, Decl(mixinAccessors1.ts, 16, 40)) +>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + + return document.createElement("select"); +>document.createElement : Symbol(Document.createElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>document : Symbol(document, Decl(lib.dom.d.ts, --, --)) +>createElement : Symbol(Document.createElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + } +} diff --git a/tests/baselines/reference/mixinAccessors1.types b/tests/baselines/reference/mixinAccessors1.types new file mode 100644 index 0000000000000..7960cfadc2be1 --- /dev/null +++ b/tests/baselines/reference/mixinAccessors1.types @@ -0,0 +1,87 @@ +//// [tests/cases/conformance/classes/mixinAccessors1.ts] //// + +=== mixinAccessors1.ts === +// https://github.com/microsoft/TypeScript/issues/58790 + +function mixin(superclass: T) { +>mixin : (superclass: T) => { new (...args: any[]): (Anonymous class); prototype: mixin.(Anonymous class); } & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>args : any[] +> : ^^^^^ +>superclass : T +> : ^ + + return class extends superclass { +>class extends superclass { get validationTarget(): HTMLElement { return document.createElement("input"); } } : { new (...args: any[]): (Anonymous class); prototype: mixin.(Anonymous class); } & T +> : ^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>superclass : {} +> : ^^ + + get validationTarget(): HTMLElement { +>validationTarget : HTMLElement +> : ^^^^^^^^^^^ + + return document.createElement("input"); +>document.createElement("input") : HTMLInputElement +> : ^^^^^^^^^^^^^^^^ +>document.createElement : { (tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; (tagName: K, options?: ElementCreationOptions): HTMLElementDeprecatedTagNameMap[K]; (tagName: string, options?: ElementCreationOptions): HTMLElement; } +> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^^ +>document : Document +> : ^^^^^^^^ +>createElement : { (tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; (tagName: K, options?: ElementCreationOptions): HTMLElementDeprecatedTagNameMap[K]; (tagName: string, options?: ElementCreationOptions): HTMLElement; } +> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^^ +>"input" : "input" +> : ^^^^^^^ + } + }; +} + +class BaseClass { +>BaseClass : BaseClass +> : ^^^^^^^^^ + + get validationTarget(): HTMLElement { +>validationTarget : HTMLElement +> : ^^^^^^^^^^^ + + return document.createElement("div"); +>document.createElement("div") : HTMLDivElement +> : ^^^^^^^^^^^^^^ +>document.createElement : { (tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; (tagName: K, options?: ElementCreationOptions): HTMLElementDeprecatedTagNameMap[K]; (tagName: string, options?: ElementCreationOptions): HTMLElement; } +> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^^ +>document : Document +> : ^^^^^^^^ +>createElement : { (tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; (tagName: K, options?: ElementCreationOptions): HTMLElementDeprecatedTagNameMap[K]; (tagName: string, options?: ElementCreationOptions): HTMLElement; } +> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^^ +>"div" : "div" +> : ^^^^^ + } +} + +class MyClass extends mixin(BaseClass) { +>MyClass : MyClass +> : ^^^^^^^ +>mixin(BaseClass) : mixin.(Anonymous class) & BaseClass +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>mixin : (superclass: T) => { new (...args: any[]): (Anonymous class); prototype: mixin.(Anonymous class); } & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>BaseClass : typeof BaseClass +> : ^^^^^^^^^^^^^^^^ + + get validationTarget(): HTMLElement { +>validationTarget : HTMLElement +> : ^^^^^^^^^^^ + + return document.createElement("select"); +>document.createElement("select") : HTMLSelectElement +> : ^^^^^^^^^^^^^^^^^ +>document.createElement : { (tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; (tagName: K, options?: ElementCreationOptions): HTMLElementDeprecatedTagNameMap[K]; (tagName: string, options?: ElementCreationOptions): HTMLElement; } +> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^^ +>document : Document +> : ^^^^^^^^ +>createElement : { (tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; (tagName: K, options?: ElementCreationOptions): HTMLElementDeprecatedTagNameMap[K]; (tagName: string, options?: ElementCreationOptions): HTMLElement; } +> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^^ +>"select" : "select" +> : ^^^^^^^^ + } +} diff --git a/tests/baselines/reference/mixinAccessors2.js b/tests/baselines/reference/mixinAccessors2.js new file mode 100644 index 0000000000000..dfb739011cd61 --- /dev/null +++ b/tests/baselines/reference/mixinAccessors2.js @@ -0,0 +1,54 @@ +//// [tests/cases/conformance/classes/mixinAccessors2.ts] //// + +//// [mixinAccessors2.ts] +function mixin(superclass: T) { + return class extends superclass { + accessor name = ""; + }; +} + +class BaseClass { + accessor name = ""; +} + +class MyClass extends mixin(BaseClass) { + accessor name = ""; +} + + +//// [mixinAccessors2.js] +"use strict"; +function mixin(superclass) { + return class extends superclass { + accessor name = ""; + }; +} +class BaseClass { + accessor name = ""; +} +class MyClass extends mixin(BaseClass) { + accessor name = ""; +} + + +//// [mixinAccessors2.d.ts] +declare function mixin(superclass: T): { + new (...args: any[]): { + get name(): string; + set name(arg: string); + }; +} & T; +declare class BaseClass { + accessor name: string; +} +declare const MyClass_base: { + new (...args: any[]): { + get name(): string; + set name(arg: string); + }; +} & typeof BaseClass; +declare class MyClass extends MyClass_base { + accessor name: string; +} diff --git a/tests/baselines/reference/mixinAccessors2.symbols b/tests/baselines/reference/mixinAccessors2.symbols new file mode 100644 index 0000000000000..1e58c90d4972d --- /dev/null +++ b/tests/baselines/reference/mixinAccessors2.symbols @@ -0,0 +1,35 @@ +//// [tests/cases/conformance/classes/mixinAccessors2.ts] //// + +=== mixinAccessors2.ts === +function mixin(superclass: T) { +>mixin : Symbol(mixin, Decl(mixinAccessors2.ts, 0, 0)) +>T : Symbol(T, Decl(mixinAccessors2.ts, 0, 15)) +>args : Symbol(args, Decl(mixinAccessors2.ts, 0, 32)) +>superclass : Symbol(superclass, Decl(mixinAccessors2.ts, 0, 55)) +>T : Symbol(T, Decl(mixinAccessors2.ts, 0, 15)) + + return class extends superclass { +>superclass : Symbol(superclass, Decl(mixinAccessors2.ts, 0, 55)) + + accessor name = ""; +>name : Symbol((Anonymous class).name, Decl(mixinAccessors2.ts, 1, 35)) + + }; +} + +class BaseClass { +>BaseClass : Symbol(BaseClass, Decl(mixinAccessors2.ts, 4, 1)) + + accessor name = ""; +>name : Symbol(BaseClass.name, Decl(mixinAccessors2.ts, 6, 17)) +} + +class MyClass extends mixin(BaseClass) { +>MyClass : Symbol(MyClass, Decl(mixinAccessors2.ts, 8, 1)) +>mixin : Symbol(mixin, Decl(mixinAccessors2.ts, 0, 0)) +>BaseClass : Symbol(BaseClass, Decl(mixinAccessors2.ts, 4, 1)) + + accessor name = ""; +>name : Symbol(MyClass.name, Decl(mixinAccessors2.ts, 10, 40)) +} + diff --git a/tests/baselines/reference/mixinAccessors2.types b/tests/baselines/reference/mixinAccessors2.types new file mode 100644 index 0000000000000..07c4b800077a9 --- /dev/null +++ b/tests/baselines/reference/mixinAccessors2.types @@ -0,0 +1,54 @@ +//// [tests/cases/conformance/classes/mixinAccessors2.ts] //// + +=== mixinAccessors2.ts === +function mixin(superclass: T) { +>mixin : (superclass: T) => { new (...args: any[]): (Anonymous class); prototype: mixin.(Anonymous class); } & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>args : any[] +> : ^^^^^ +>superclass : T +> : ^ + + return class extends superclass { +>class extends superclass { accessor name = ""; } : { new (...args: any[]): (Anonymous class); prototype: mixin.(Anonymous class); } & T +> : ^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>superclass : {} +> : ^^ + + accessor name = ""; +>name : string +> : ^^^^^^ +>"" : "" +> : ^^ + + }; +} + +class BaseClass { +>BaseClass : BaseClass +> : ^^^^^^^^^ + + accessor name = ""; +>name : string +> : ^^^^^^ +>"" : "" +> : ^^ +} + +class MyClass extends mixin(BaseClass) { +>MyClass : MyClass +> : ^^^^^^^ +>mixin(BaseClass) : mixin.(Anonymous class) & BaseClass +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>mixin : (superclass: T) => { new (...args: any[]): (Anonymous class); prototype: mixin.(Anonymous class); } & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>BaseClass : typeof BaseClass +> : ^^^^^^^^^^^^^^^^ + + accessor name = ""; +>name : string +> : ^^^^^^ +>"" : "" +> : ^^ +} + diff --git a/tests/baselines/reference/mixinAccessors3.errors.txt b/tests/baselines/reference/mixinAccessors3.errors.txt new file mode 100644 index 0000000000000..9e5185f43d822 --- /dev/null +++ b/tests/baselines/reference/mixinAccessors3.errors.txt @@ -0,0 +1,25 @@ +mixinAccessors3.ts(15,7): error TS2611: 'name' is defined as a property in class 'mixin.(Anonymous class) & BaseClass', but is overridden here in 'MyClass' as an accessor. + + +==== mixinAccessors3.ts (1 errors) ==== + function mixin(superclass: T) { + return class extends superclass { + get name() { + return ""; + } + }; + } + + class BaseClass { + set name(v: string) {} + } + + // error + class MyClass extends mixin(BaseClass) { + get name() { + ~~~~ +!!! error TS2611: 'name' is defined as a property in class 'mixin.(Anonymous class) & BaseClass', but is overridden here in 'MyClass' as an accessor. + return ""; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/mixinAccessors3.js b/tests/baselines/reference/mixinAccessors3.js new file mode 100644 index 0000000000000..0b415f0cc0d6a --- /dev/null +++ b/tests/baselines/reference/mixinAccessors3.js @@ -0,0 +1,62 @@ +//// [tests/cases/conformance/classes/mixinAccessors3.ts] //// + +//// [mixinAccessors3.ts] +function mixin(superclass: T) { + return class extends superclass { + get name() { + return ""; + } + }; +} + +class BaseClass { + set name(v: string) {} +} + +// error +class MyClass extends mixin(BaseClass) { + get name() { + return ""; + } +} + + +//// [mixinAccessors3.js] +"use strict"; +function mixin(superclass) { + return class extends superclass { + get name() { + return ""; + } + }; +} +class BaseClass { + set name(v) { } +} +// error +class MyClass extends mixin(BaseClass) { + get name() { + return ""; + } +} + + +//// [mixinAccessors3.d.ts] +declare function mixin(superclass: T): { + new (...args: any[]): { + get name(): string; + }; +} & T; +declare class BaseClass { + set name(v: string); +} +declare const MyClass_base: { + new (...args: any[]): { + get name(): string; + }; +} & typeof BaseClass; +declare class MyClass extends MyClass_base { + get name(): string; +} diff --git a/tests/baselines/reference/mixinAccessors3.symbols b/tests/baselines/reference/mixinAccessors3.symbols new file mode 100644 index 0000000000000..cb1357dbf4e75 --- /dev/null +++ b/tests/baselines/reference/mixinAccessors3.symbols @@ -0,0 +1,42 @@ +//// [tests/cases/conformance/classes/mixinAccessors3.ts] //// + +=== mixinAccessors3.ts === +function mixin(superclass: T) { +>mixin : Symbol(mixin, Decl(mixinAccessors3.ts, 0, 0)) +>T : Symbol(T, Decl(mixinAccessors3.ts, 0, 15)) +>args : Symbol(args, Decl(mixinAccessors3.ts, 0, 32)) +>superclass : Symbol(superclass, Decl(mixinAccessors3.ts, 0, 55)) +>T : Symbol(T, Decl(mixinAccessors3.ts, 0, 15)) + + return class extends superclass { +>superclass : Symbol(superclass, Decl(mixinAccessors3.ts, 0, 55)) + + get name() { +>name : Symbol((Anonymous class).name, Decl(mixinAccessors3.ts, 1, 35)) + + return ""; + } + }; +} + +class BaseClass { +>BaseClass : Symbol(BaseClass, Decl(mixinAccessors3.ts, 6, 1)) + + set name(v: string) {} +>name : Symbol(BaseClass.name, Decl(mixinAccessors3.ts, 8, 17)) +>v : Symbol(v, Decl(mixinAccessors3.ts, 9, 11)) +} + +// error +class MyClass extends mixin(BaseClass) { +>MyClass : Symbol(MyClass, Decl(mixinAccessors3.ts, 10, 1)) +>mixin : Symbol(mixin, Decl(mixinAccessors3.ts, 0, 0)) +>BaseClass : Symbol(BaseClass, Decl(mixinAccessors3.ts, 6, 1)) + + get name() { +>name : Symbol(MyClass.name, Decl(mixinAccessors3.ts, 13, 40)) + + return ""; + } +} + diff --git a/tests/baselines/reference/mixinAccessors3.types b/tests/baselines/reference/mixinAccessors3.types new file mode 100644 index 0000000000000..b0dc060aeef05 --- /dev/null +++ b/tests/baselines/reference/mixinAccessors3.types @@ -0,0 +1,60 @@ +//// [tests/cases/conformance/classes/mixinAccessors3.ts] //// + +=== mixinAccessors3.ts === +function mixin(superclass: T) { +>mixin : (superclass: T) => { new (...args: any[]): (Anonymous class); prototype: mixin.(Anonymous class); } & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>args : any[] +> : ^^^^^ +>superclass : T +> : ^ + + return class extends superclass { +>class extends superclass { get name() { return ""; } } : { new (...args: any[]): (Anonymous class); prototype: mixin.(Anonymous class); } & T +> : ^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>superclass : {} +> : ^^ + + get name() { +>name : string +> : ^^^^^^ + + return ""; +>"" : "" +> : ^^ + } + }; +} + +class BaseClass { +>BaseClass : BaseClass +> : ^^^^^^^^^ + + set name(v: string) {} +>name : string +> : ^^^^^^ +>v : string +> : ^^^^^^ +} + +// error +class MyClass extends mixin(BaseClass) { +>MyClass : MyClass +> : ^^^^^^^ +>mixin(BaseClass) : mixin.(Anonymous class) & BaseClass +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>mixin : (superclass: T) => { new (...args: any[]): (Anonymous class); prototype: mixin.(Anonymous class); } & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>BaseClass : typeof BaseClass +> : ^^^^^^^^^^^^^^^^ + + get name() { +>name : string +> : ^^^^^^ + + return ""; +>"" : "" +> : ^^ + } +} + diff --git a/tests/baselines/reference/mixinAccessors4.js b/tests/baselines/reference/mixinAccessors4.js new file mode 100644 index 0000000000000..dae2068843a02 --- /dev/null +++ b/tests/baselines/reference/mixinAccessors4.js @@ -0,0 +1,68 @@ +//// [tests/cases/conformance/classes/mixinAccessors4.ts] //// + +//// [mixinAccessors4.ts] +// https://github.com/microsoft/TypeScript/issues/44938 + +class A { + constructor(...args: any[]) {} + get myName(): string { + return "A"; + } +} + +function Mixin(Super: T) { + return class B extends Super { + get myName(): string { + return "B"; + } + }; +} + +class C extends Mixin(A) { + get myName(): string { + return "C"; + } +} + + +//// [mixinAccessors4.js] +"use strict"; +// https://github.com/microsoft/TypeScript/issues/44938 +class A { + constructor(...args) { } + get myName() { + return "A"; + } +} +function Mixin(Super) { + return class B extends Super { + get myName() { + return "B"; + } + }; +} +class C extends Mixin(A) { + get myName() { + return "C"; + } +} + + +//// [mixinAccessors4.d.ts] +declare class A { + constructor(...args: any[]); + get myName(): string; +} +declare function Mixin(Super: T): { + new (...args: any[]): { + get myName(): string; + }; +} & T; +declare const C_base: { + new (...args: any[]): { + get myName(): string; + }; +} & typeof A; +declare class C extends C_base { + get myName(): string; +} diff --git a/tests/baselines/reference/mixinAccessors4.symbols b/tests/baselines/reference/mixinAccessors4.symbols new file mode 100644 index 0000000000000..0b34c00c0e541 --- /dev/null +++ b/tests/baselines/reference/mixinAccessors4.symbols @@ -0,0 +1,49 @@ +//// [tests/cases/conformance/classes/mixinAccessors4.ts] //// + +=== mixinAccessors4.ts === +// https://github.com/microsoft/TypeScript/issues/44938 + +class A { +>A : Symbol(A, Decl(mixinAccessors4.ts, 0, 0)) + + constructor(...args: any[]) {} +>args : Symbol(args, Decl(mixinAccessors4.ts, 3, 14)) + + get myName(): string { +>myName : Symbol(A.myName, Decl(mixinAccessors4.ts, 3, 32)) + + return "A"; + } +} + +function Mixin(Super: T) { +>Mixin : Symbol(Mixin, Decl(mixinAccessors4.ts, 7, 1)) +>T : Symbol(T, Decl(mixinAccessors4.ts, 9, 15)) +>A : Symbol(A, Decl(mixinAccessors4.ts, 0, 0)) +>Super : Symbol(Super, Decl(mixinAccessors4.ts, 9, 35)) +>T : Symbol(T, Decl(mixinAccessors4.ts, 9, 15)) + + return class B extends Super { +>B : Symbol(B, Decl(mixinAccessors4.ts, 10, 8)) +>Super : Symbol(Super, Decl(mixinAccessors4.ts, 9, 35)) + + get myName(): string { +>myName : Symbol(B.myName, Decl(mixinAccessors4.ts, 10, 32)) + + return "B"; + } + }; +} + +class C extends Mixin(A) { +>C : Symbol(C, Decl(mixinAccessors4.ts, 15, 1)) +>Mixin : Symbol(Mixin, Decl(mixinAccessors4.ts, 7, 1)) +>A : Symbol(A, Decl(mixinAccessors4.ts, 0, 0)) + + get myName(): string { +>myName : Symbol(C.myName, Decl(mixinAccessors4.ts, 17, 26)) + + return "C"; + } +} + diff --git a/tests/baselines/reference/mixinAccessors4.types b/tests/baselines/reference/mixinAccessors4.types new file mode 100644 index 0000000000000..07fd027758a50 --- /dev/null +++ b/tests/baselines/reference/mixinAccessors4.types @@ -0,0 +1,70 @@ +//// [tests/cases/conformance/classes/mixinAccessors4.ts] //// + +=== mixinAccessors4.ts === +// https://github.com/microsoft/TypeScript/issues/44938 + +class A { +>A : A +> : ^ + + constructor(...args: any[]) {} +>args : any[] +> : ^^^^^ + + get myName(): string { +>myName : string +> : ^^^^^^ + + return "A"; +>"A" : "A" +> : ^^^ + } +} + +function Mixin(Super: T) { +>Mixin : (Super: T) => { new (...args: any[]): B; prototype: Mixin.B; } & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>A : typeof A +> : ^^^^^^^^ +>Super : T +> : ^ + + return class B extends Super { +>class B extends Super { get myName(): string { return "B"; } } : { new (...args: any[]): B; prototype: Mixin.B; } & T +> : ^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>B : { new (...args: any[]): B; prototype: Mixin.B; } & T +> : ^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Super : A +> : ^ + + get myName(): string { +>myName : string +> : ^^^^^^ + + return "B"; +>"B" : "B" +> : ^^^ + } + }; +} + +class C extends Mixin(A) { +>C : C +> : ^ +>Mixin(A) : Mixin.B & A +> : ^^^^^^^^^^^^^^^^^^^^^ +>Mixin : (Super: T) => { new (...args: any[]): B; prototype: Mixin.B; } & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>A : typeof A +> : ^^^^^^^^ + + get myName(): string { +>myName : string +> : ^^^^^^ + + return "C"; +>"C" : "C" +> : ^^^ + } +} + diff --git a/tests/cases/conformance/classes/mixinAccessors1.ts b/tests/cases/conformance/classes/mixinAccessors1.ts new file mode 100644 index 0000000000000..b8c0a451767cc --- /dev/null +++ b/tests/cases/conformance/classes/mixinAccessors1.ts @@ -0,0 +1,26 @@ +// @strict: true +// @target: esnext +// @lib: dom,esnext +// @declaration: true + +// https://github.com/microsoft/TypeScript/issues/58790 + +function mixin(superclass: T) { + return class extends superclass { + get validationTarget(): HTMLElement { + return document.createElement("input"); + } + }; +} + +class BaseClass { + get validationTarget(): HTMLElement { + return document.createElement("div"); + } +} + +class MyClass extends mixin(BaseClass) { + get validationTarget(): HTMLElement { + return document.createElement("select"); + } +} \ No newline at end of file diff --git a/tests/cases/conformance/classes/mixinAccessors2.ts b/tests/cases/conformance/classes/mixinAccessors2.ts new file mode 100644 index 0000000000000..afd15551b8e3d --- /dev/null +++ b/tests/cases/conformance/classes/mixinAccessors2.ts @@ -0,0 +1,17 @@ +// @strict: true +// @target: esnext +// @declaration: true + +function mixin(superclass: T) { + return class extends superclass { + accessor name = ""; + }; +} + +class BaseClass { + accessor name = ""; +} + +class MyClass extends mixin(BaseClass) { + accessor name = ""; +} diff --git a/tests/cases/conformance/classes/mixinAccessors3.ts b/tests/cases/conformance/classes/mixinAccessors3.ts new file mode 100644 index 0000000000000..2524df67d6915 --- /dev/null +++ b/tests/cases/conformance/classes/mixinAccessors3.ts @@ -0,0 +1,22 @@ +// @strict: true +// @target: esnext +// @declaration: true + +function mixin(superclass: T) { + return class extends superclass { + get name() { + return ""; + } + }; +} + +class BaseClass { + set name(v: string) {} +} + +// error +class MyClass extends mixin(BaseClass) { + get name() { + return ""; + } +} diff --git a/tests/cases/conformance/classes/mixinAccessors4.ts b/tests/cases/conformance/classes/mixinAccessors4.ts new file mode 100644 index 0000000000000..1109aa6273d26 --- /dev/null +++ b/tests/cases/conformance/classes/mixinAccessors4.ts @@ -0,0 +1,26 @@ +// @strict: true +// @target: esnext +// @declaration: true + +// https://github.com/microsoft/TypeScript/issues/44938 + +class A { + constructor(...args: any[]) {} + get myName(): string { + return "A"; + } +} + +function Mixin(Super: T) { + return class B extends Super { + get myName(): string { + return "B"; + } + }; +} + +class C extends Mixin(A) { + get myName(): string { + return "C"; + } +} From 161e1ca346581303178e2dd930eec6a86ea56973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 1 Jan 2025 21:26:25 +0100 Subject: [PATCH 3/3] fixed a thing --- src/compiler/checker.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8983bdd8b9002..ae705e009e4e5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12313,13 +12313,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { */ function getWriteTypeOfSymbol(symbol: Symbol): Type { const checkFlags = getCheckFlags(symbol); + if (checkFlags & CheckFlags.SyntheticProperty) { + return checkFlags & CheckFlags.DeferredType ? + getWriteTypeOfSymbolWithDeferredType(symbol) || getTypeOfSymbolWithDeferredType(symbol) : + // NOTE: cast to TransientSymbol should be safe because only TransientSymbols can have CheckFlags.SyntheticProperty + (symbol as TransientSymbol).links.writeType || (symbol as TransientSymbol).links.type!; + } if (symbol.flags & SymbolFlags.Property) { - return checkFlags & CheckFlags.SyntheticProperty ? - checkFlags & CheckFlags.DeferredType ? - getWriteTypeOfSymbolWithDeferredType(symbol) || getTypeOfSymbolWithDeferredType(symbol) : - // NOTE: cast to TransientSymbol should be safe because only TransientSymbols can have CheckFlags.SyntheticProperty - (symbol as TransientSymbol).links.writeType || (symbol as TransientSymbol).links.type! : - removeMissingType(getTypeOfSymbol(symbol), !!(symbol.flags & SymbolFlags.Optional)); + return removeMissingType(getTypeOfSymbol(symbol), !!(symbol.flags & SymbolFlags.Optional)); } if (symbol.flags & SymbolFlags.Accessor) { return checkFlags & CheckFlags.Instantiated ?