diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7bcc6f5bdc65a..a73066db6368c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7555,7 +7555,7 @@ namespace ts { ...!length(baseTypes) ? [] : [factory.createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))], ...!length(implementsExpressions) ? [] : [factory.createHeritageClause(SyntaxKind.ImplementsKeyword, implementsExpressions)] ]; - const symbolProps = getNonInterhitedProperties(classType, baseTypes, getPropertiesOfType(classType)); + const symbolProps = getNonInheritedProperties(classType, baseTypes, getPropertiesOfType(classType)); const publicSymbolProps = filter(symbolProps, s => { // `valueDeclaration` could be undefined if inherited from // a union/intersection base type, but inherited properties @@ -39949,10 +39949,13 @@ namespace ts { const derivedPropertyFlags = derived.flags & SymbolFlags.PropertyOrAccessor; if (basePropertyFlags && derivedPropertyFlags) { // property/accessor is overridden with property/accessor - if (baseDeclarationFlags & ModifierFlags.Abstract && !(base.valueDeclaration && isPropertyDeclaration(base.valueDeclaration) && base.valueDeclaration.initializer) - || base.valueDeclaration && base.valueDeclaration.parent.kind === SyntaxKind.InterfaceDeclaration + if ((getCheckFlags(base) & CheckFlags.Synthetic + ? base.declarations?.some(d => isPropertyAbstractOrInterface(d, baseDeclarationFlags)) + : base.declarations?.every(d => isPropertyAbstractOrInterface(d, baseDeclarationFlags))) + || getCheckFlags(base) & CheckFlags.Mapped || derived.valueDeclaration && isBinaryExpression(derived.valueDeclaration)) { // when the base property is abstract or from an interface, base/derived flags don't need to match + // for intersection properties, this must be true of *any* of the declarations, for others it must be true of *all* // same when the derived property is from an assignment continue; } @@ -40010,7 +40013,12 @@ namespace ts { } } - function getNonInterhitedProperties(type: InterfaceType, baseTypes: BaseType[], properties: Symbol[]) { + function isPropertyAbstractOrInterface(declaration: Declaration, baseDeclarationFlags: ModifierFlags) { + return baseDeclarationFlags & ModifierFlags.Abstract && (!isPropertyDeclaration(declaration) || !declaration.initializer) + || isInterfaceDeclaration(declaration.parent); + } + + function getNonInheritedProperties(type: InterfaceType, baseTypes: BaseType[], properties: Symbol[]) { if (!length(baseTypes)) { return properties; } diff --git a/tests/baselines/reference/accessorsOverrideProperty8.js b/tests/baselines/reference/accessorsOverrideProperty8.js new file mode 100644 index 0000000000000..bd51c4b9afbaa --- /dev/null +++ b/tests/baselines/reference/accessorsOverrideProperty8.js @@ -0,0 +1,50 @@ +//// [accessorsOverrideProperty8.ts] +type Types = 'boolean' | 'unknown' | 'string'; + +type Properties = { + readonly [key in keyof T]: T[key] extends 'boolean' ? boolean : T[key] extends 'string' ? string : unknown +} + +type AnyCtor

= new (...a: any[]) => P + +declare function classWithProperties(properties: T, klass: AnyCtor

): { + new(): P & Properties; + prototype: P & Properties +}; + +const Base = classWithProperties({ + get x() { return 'boolean' as const }, + y: 'string', +}, class Base { +}); + +class MyClass extends Base { + get x() { + return false; + } + get y() { + return 'hi' + } +} + +const mine = new MyClass(); +const value = mine.x; + + + +//// [accessorsOverrideProperty8.js] +const Base = classWithProperties({ + get x() { return 'boolean'; }, + y: 'string', +}, class Base { +}); +class MyClass extends Base { + get x() { + return false; + } + get y() { + return 'hi'; + } +} +const mine = new MyClass(); +const value = mine.x; diff --git a/tests/baselines/reference/accessorsOverrideProperty8.symbols b/tests/baselines/reference/accessorsOverrideProperty8.symbols new file mode 100644 index 0000000000000..7e60b8c582de5 --- /dev/null +++ b/tests/baselines/reference/accessorsOverrideProperty8.symbols @@ -0,0 +1,93 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty8.ts === +type Types = 'boolean' | 'unknown' | 'string'; +>Types : Symbol(Types, Decl(accessorsOverrideProperty8.ts, 0, 0)) + +type Properties = { +>Properties : Symbol(Properties, Decl(accessorsOverrideProperty8.ts, 0, 46)) +>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 2, 16)) +>key : Symbol(key, Decl(accessorsOverrideProperty8.ts, 2, 29)) +>Types : Symbol(Types, Decl(accessorsOverrideProperty8.ts, 0, 0)) + + readonly [key in keyof T]: T[key] extends 'boolean' ? boolean : T[key] extends 'string' ? string : unknown +>key : Symbol(key, Decl(accessorsOverrideProperty8.ts, 3, 14)) +>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 2, 16)) +>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 2, 16)) +>key : Symbol(key, Decl(accessorsOverrideProperty8.ts, 3, 14)) +>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 2, 16)) +>key : Symbol(key, Decl(accessorsOverrideProperty8.ts, 3, 14)) +} + +type AnyCtor

= new (...a: any[]) => P +>AnyCtor : Symbol(AnyCtor, Decl(accessorsOverrideProperty8.ts, 4, 1)) +>P : Symbol(P, Decl(accessorsOverrideProperty8.ts, 6, 13)) +>a : Symbol(a, Decl(accessorsOverrideProperty8.ts, 6, 38)) +>P : Symbol(P, Decl(accessorsOverrideProperty8.ts, 6, 13)) + +declare function classWithProperties(properties: T, klass: AnyCtor

): { +>classWithProperties : Symbol(classWithProperties, Decl(accessorsOverrideProperty8.ts, 6, 55)) +>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 8, 37)) +>key : Symbol(key, Decl(accessorsOverrideProperty8.ts, 8, 50)) +>Types : Symbol(Types, Decl(accessorsOverrideProperty8.ts, 0, 0)) +>P : Symbol(P, Decl(accessorsOverrideProperty8.ts, 8, 72)) +>properties : Symbol(properties, Decl(accessorsOverrideProperty8.ts, 8, 91)) +>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 8, 37)) +>klass : Symbol(klass, Decl(accessorsOverrideProperty8.ts, 8, 105)) +>AnyCtor : Symbol(AnyCtor, Decl(accessorsOverrideProperty8.ts, 4, 1)) +>P : Symbol(P, Decl(accessorsOverrideProperty8.ts, 8, 72)) + + new(): P & Properties; +>P : Symbol(P, Decl(accessorsOverrideProperty8.ts, 8, 72)) +>Properties : Symbol(Properties, Decl(accessorsOverrideProperty8.ts, 0, 46)) +>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 8, 37)) + + prototype: P & Properties +>prototype : Symbol(prototype, Decl(accessorsOverrideProperty8.ts, 9, 29)) +>P : Symbol(P, Decl(accessorsOverrideProperty8.ts, 8, 72)) +>Properties : Symbol(Properties, Decl(accessorsOverrideProperty8.ts, 0, 46)) +>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 8, 37)) + +}; + +const Base = classWithProperties({ +>Base : Symbol(Base, Decl(accessorsOverrideProperty8.ts, 13, 5)) +>classWithProperties : Symbol(classWithProperties, Decl(accessorsOverrideProperty8.ts, 6, 55)) + + get x() { return 'boolean' as const }, +>x : Symbol(x, Decl(accessorsOverrideProperty8.ts, 13, 34)) +>const : Symbol(const) + + y: 'string', +>y : Symbol(y, Decl(accessorsOverrideProperty8.ts, 14, 42)) + +}, class Base { +>Base : Symbol(Base, Decl(accessorsOverrideProperty8.ts, 16, 2)) + +}); + +class MyClass extends Base { +>MyClass : Symbol(MyClass, Decl(accessorsOverrideProperty8.ts, 17, 3)) +>Base : Symbol(Base, Decl(accessorsOverrideProperty8.ts, 13, 5)) + + get x() { +>x : Symbol(MyClass.x, Decl(accessorsOverrideProperty8.ts, 19, 28)) + + return false; + } + get y() { +>y : Symbol(MyClass.y, Decl(accessorsOverrideProperty8.ts, 22, 5)) + + return 'hi' + } +} + +const mine = new MyClass(); +>mine : Symbol(mine, Decl(accessorsOverrideProperty8.ts, 28, 5)) +>MyClass : Symbol(MyClass, Decl(accessorsOverrideProperty8.ts, 17, 3)) + +const value = mine.x; +>value : Symbol(value, Decl(accessorsOverrideProperty8.ts, 29, 5)) +>mine.x : Symbol(MyClass.x, Decl(accessorsOverrideProperty8.ts, 19, 28)) +>mine : Symbol(mine, Decl(accessorsOverrideProperty8.ts, 28, 5)) +>x : Symbol(MyClass.x, Decl(accessorsOverrideProperty8.ts, 19, 28)) + + diff --git a/tests/baselines/reference/accessorsOverrideProperty8.types b/tests/baselines/reference/accessorsOverrideProperty8.types new file mode 100644 index 0000000000000..1379593d280a4 --- /dev/null +++ b/tests/baselines/reference/accessorsOverrideProperty8.types @@ -0,0 +1,78 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty8.ts === +type Types = 'boolean' | 'unknown' | 'string'; +>Types : "string" | "boolean" | "unknown" + +type Properties = { +>Properties : Properties +>key : string + + readonly [key in keyof T]: T[key] extends 'boolean' ? boolean : T[key] extends 'string' ? string : unknown +} + +type AnyCtor

= new (...a: any[]) => P +>AnyCtor : AnyCtor

+>a : any[] + +declare function classWithProperties(properties: T, klass: AnyCtor

): { +>classWithProperties : (properties: T, klass: AnyCtor

) => { new (): P & Properties; prototype: P & Properties;} +>key : string +>properties : T +>klass : AnyCtor

+ + new(): P & Properties; + prototype: P & Properties +>prototype : P & Properties + +}; + +const Base = classWithProperties({ +>Base : { new (): Base & Properties<{ readonly x: "boolean"; y: "string"; }>; prototype: Base & Properties<{ readonly x: "boolean"; y: "string"; }>; } +>classWithProperties({ get x() { return 'boolean' as const }, y: 'string',}, class Base {}) : { new (): Base & Properties<{ readonly x: "boolean"; y: "string"; }>; prototype: Base & Properties<{ readonly x: "boolean"; y: "string"; }>; } +>classWithProperties : (properties: T, klass: AnyCtor

) => { new (): P & Properties; prototype: P & Properties; } +>{ get x() { return 'boolean' as const }, y: 'string',} : { readonly x: "boolean"; y: "string"; } + + get x() { return 'boolean' as const }, +>x : "boolean" +>'boolean' as const : "boolean" +>'boolean' : "boolean" + + y: 'string', +>y : "string" +>'string' : "string" + +}, class Base { +>class Base {} : typeof Base +>Base : typeof Base + +}); + +class MyClass extends Base { +>MyClass : MyClass +>Base : Base & Properties<{ readonly x: "boolean"; y: "string"; }> + + get x() { +>x : boolean + + return false; +>false : false + } + get y() { +>y : string + + return 'hi' +>'hi' : "hi" + } +} + +const mine = new MyClass(); +>mine : MyClass +>new MyClass() : MyClass +>MyClass : typeof MyClass + +const value = mine.x; +>value : boolean +>mine.x : boolean +>mine : MyClass +>x : boolean + + diff --git a/tests/baselines/reference/accessorsOverrideProperty9.js b/tests/baselines/reference/accessorsOverrideProperty9.js new file mode 100644 index 0000000000000..a1b8d7bde87aa --- /dev/null +++ b/tests/baselines/reference/accessorsOverrideProperty9.js @@ -0,0 +1,79 @@ +//// [accessorsOverrideProperty9.ts] +// #41347, based on microsoft/rushstack + +// Mixin utilities +export type Constructor = new (...args: any[]) => T; +export type PropertiesOf = { [K in keyof T]: T[K] }; + +interface IApiItemConstructor extends Constructor, PropertiesOf {} + +// Base class +class ApiItem { + public get members(): ReadonlyArray { + return []; + } +} + +// Normal subclass +class ApiEnumMember extends ApiItem { +} + +// Mixin base class +interface ApiItemContainerMixin extends ApiItem { + readonly members: ReadonlyArray; +} + +function ApiItemContainerMixin( + baseClass: TBaseClass +): TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) { + abstract class MixedClass extends baseClass implements ApiItemContainerMixin { + public constructor(...args: any[]) { + super(...args); + } + + public get members(): ReadonlyArray { + return []; + } + } + + return MixedClass; +} + +// Subclass inheriting from mixin +export class ApiEnum extends ApiItemContainerMixin(ApiItem) { + // This worked prior to TypeScript 4.0: + public get members(): ReadonlyArray { + return []; + } +} + + +//// [accessorsOverrideProperty9.js] +// #41347, based on microsoft/rushstack +// Base class +class ApiItem { + get members() { + return []; + } +} +// Normal subclass +class ApiEnumMember extends ApiItem { +} +function ApiItemContainerMixin(baseClass) { + class MixedClass extends baseClass { + constructor(...args) { + super(...args); + } + get members() { + return []; + } + } + return MixedClass; +} +// Subclass inheriting from mixin +export class ApiEnum extends ApiItemContainerMixin(ApiItem) { + // This worked prior to TypeScript 4.0: + get members() { + return []; + } +} diff --git a/tests/baselines/reference/accessorsOverrideProperty9.symbols b/tests/baselines/reference/accessorsOverrideProperty9.symbols new file mode 100644 index 0000000000000..5195876798a4f --- /dev/null +++ b/tests/baselines/reference/accessorsOverrideProperty9.symbols @@ -0,0 +1,111 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty9.ts === +// #41347, based on microsoft/rushstack + +// Mixin utilities +export type Constructor = new (...args: any[]) => T; +>Constructor : Symbol(Constructor, Decl(accessorsOverrideProperty9.ts, 0, 0)) +>T : Symbol(T, Decl(accessorsOverrideProperty9.ts, 3, 24)) +>args : Symbol(args, Decl(accessorsOverrideProperty9.ts, 3, 39)) +>T : Symbol(T, Decl(accessorsOverrideProperty9.ts, 3, 24)) + +export type PropertiesOf = { [K in keyof T]: T[K] }; +>PropertiesOf : Symbol(PropertiesOf, Decl(accessorsOverrideProperty9.ts, 3, 60)) +>T : Symbol(T, Decl(accessorsOverrideProperty9.ts, 4, 25)) +>K : Symbol(K, Decl(accessorsOverrideProperty9.ts, 4, 33)) +>T : Symbol(T, Decl(accessorsOverrideProperty9.ts, 4, 25)) +>T : Symbol(T, Decl(accessorsOverrideProperty9.ts, 4, 25)) +>K : Symbol(K, Decl(accessorsOverrideProperty9.ts, 4, 33)) + +interface IApiItemConstructor extends Constructor, PropertiesOf {} +>IApiItemConstructor : Symbol(IApiItemConstructor, Decl(accessorsOverrideProperty9.ts, 4, 55)) +>Constructor : Symbol(Constructor, Decl(accessorsOverrideProperty9.ts, 0, 0)) +>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91)) +>PropertiesOf : Symbol(PropertiesOf, Decl(accessorsOverrideProperty9.ts, 3, 60)) +>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91)) + +// Base class +class ApiItem { +>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91)) + + public get members(): ReadonlyArray { +>members : Symbol(ApiItem.members, Decl(accessorsOverrideProperty9.ts, 9, 15)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2016.array.include.d.ts, --, --)) +>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91)) + + return []; + } +} + +// Normal subclass +class ApiEnumMember extends ApiItem { +>ApiEnumMember : Symbol(ApiEnumMember, Decl(accessorsOverrideProperty9.ts, 13, 1)) +>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91)) +} + +// Mixin base class +interface ApiItemContainerMixin extends ApiItem { +>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty9.ts, 22, 1), Decl(accessorsOverrideProperty9.ts, 17, 1)) +>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91)) + + readonly members: ReadonlyArray; +>members : Symbol(ApiItemContainerMixin.members, Decl(accessorsOverrideProperty9.ts, 20, 49)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2016.array.include.d.ts, --, --)) +>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91)) +} + +function ApiItemContainerMixin( +>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty9.ts, 22, 1), Decl(accessorsOverrideProperty9.ts, 17, 1)) +>TBaseClass : Symbol(TBaseClass, Decl(accessorsOverrideProperty9.ts, 24, 31)) +>IApiItemConstructor : Symbol(IApiItemConstructor, Decl(accessorsOverrideProperty9.ts, 4, 55)) + + baseClass: TBaseClass +>baseClass : Symbol(baseClass, Decl(accessorsOverrideProperty9.ts, 24, 71)) +>TBaseClass : Symbol(TBaseClass, Decl(accessorsOverrideProperty9.ts, 24, 31)) + +): TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) { +>TBaseClass : Symbol(TBaseClass, Decl(accessorsOverrideProperty9.ts, 24, 31)) +>args : Symbol(args, Decl(accessorsOverrideProperty9.ts, 26, 22)) +>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty9.ts, 22, 1), Decl(accessorsOverrideProperty9.ts, 17, 1)) + + abstract class MixedClass extends baseClass implements ApiItemContainerMixin { +>MixedClass : Symbol(MixedClass, Decl(accessorsOverrideProperty9.ts, 26, 65)) +>baseClass : Symbol(baseClass, Decl(accessorsOverrideProperty9.ts, 24, 71)) +>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty9.ts, 22, 1), Decl(accessorsOverrideProperty9.ts, 17, 1)) + + public constructor(...args: any[]) { +>args : Symbol(args, Decl(accessorsOverrideProperty9.ts, 28, 23)) + + super(...args); +>super : Symbol(TBaseClass, Decl(accessorsOverrideProperty9.ts, 24, 31)) +>args : Symbol(args, Decl(accessorsOverrideProperty9.ts, 28, 23)) + } + + public get members(): ReadonlyArray { +>members : Symbol(MixedClass.members, Decl(accessorsOverrideProperty9.ts, 30, 5)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2016.array.include.d.ts, --, --)) +>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91)) + + return []; + } + } + + return MixedClass; +>MixedClass : Symbol(MixedClass, Decl(accessorsOverrideProperty9.ts, 26, 65)) +} + +// Subclass inheriting from mixin +export class ApiEnum extends ApiItemContainerMixin(ApiItem) { +>ApiEnum : Symbol(ApiEnum, Decl(accessorsOverrideProperty9.ts, 38, 1)) +>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty9.ts, 22, 1), Decl(accessorsOverrideProperty9.ts, 17, 1)) +>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91)) + + // This worked prior to TypeScript 4.0: + public get members(): ReadonlyArray { +>members : Symbol(ApiEnum.members, Decl(accessorsOverrideProperty9.ts, 41, 61)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2016.array.include.d.ts, --, --)) +>ApiEnumMember : Symbol(ApiEnumMember, Decl(accessorsOverrideProperty9.ts, 13, 1)) + + return []; + } +} + diff --git a/tests/baselines/reference/accessorsOverrideProperty9.types b/tests/baselines/reference/accessorsOverrideProperty9.types new file mode 100644 index 0000000000000..dcbc3523e10cd --- /dev/null +++ b/tests/baselines/reference/accessorsOverrideProperty9.types @@ -0,0 +1,89 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty9.ts === +// #41347, based on microsoft/rushstack + +// Mixin utilities +export type Constructor = new (...args: any[]) => T; +>Constructor : Constructor +>args : any[] + +export type PropertiesOf = { [K in keyof T]: T[K] }; +>PropertiesOf : PropertiesOf + +interface IApiItemConstructor extends Constructor, PropertiesOf {} +>ApiItem : typeof ApiItem + +// Base class +class ApiItem { +>ApiItem : ApiItem + + public get members(): ReadonlyArray { +>members : readonly ApiItem[] + + return []; +>[] : never[] + } +} + +// Normal subclass +class ApiEnumMember extends ApiItem { +>ApiEnumMember : ApiEnumMember +>ApiItem : ApiItem +} + +// Mixin base class +interface ApiItemContainerMixin extends ApiItem { + readonly members: ReadonlyArray; +>members : readonly ApiItem[] +} + +function ApiItemContainerMixin( +>ApiItemContainerMixin : (baseClass: TBaseClass) => TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) + + baseClass: TBaseClass +>baseClass : TBaseClass + +): TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) { +>args : any[] + + abstract class MixedClass extends baseClass implements ApiItemContainerMixin { +>MixedClass : MixedClass +>baseClass : ApiItem + + public constructor(...args: any[]) { +>args : any[] + + super(...args); +>super(...args) : void +>super : TBaseClass +>...args : any +>args : any[] + } + + public get members(): ReadonlyArray { +>members : readonly ApiItem[] + + return []; +>[] : never[] + } + } + + return MixedClass; +>MixedClass : ((abstract new (...args: any[]) => MixedClass) & { prototype: ApiItemContainerMixin.MixedClass; }) & TBaseClass +} + +// Subclass inheriting from mixin +export class ApiEnum extends ApiItemContainerMixin(ApiItem) { +>ApiEnum : ApiEnum +>ApiItemContainerMixin(ApiItem) : ApiItem & ApiItemContainerMixin +>ApiItemContainerMixin : (baseClass: TBaseClass) => TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) +>ApiItem : typeof ApiItem + + // This worked prior to TypeScript 4.0: + public get members(): ReadonlyArray { +>members : readonly ApiEnumMember[] + + return []; +>[] : never[] + } +} + diff --git a/tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty8.ts b/tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty8.ts new file mode 100644 index 0000000000000..c8b02945cad08 --- /dev/null +++ b/tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty8.ts @@ -0,0 +1,32 @@ +// @target: es2019 +type Types = 'boolean' | 'unknown' | 'string'; + +type Properties = { + readonly [key in keyof T]: T[key] extends 'boolean' ? boolean : T[key] extends 'string' ? string : unknown +} + +type AnyCtor

= new (...a: any[]) => P + +declare function classWithProperties(properties: T, klass: AnyCtor

): { + new(): P & Properties; + prototype: P & Properties +}; + +const Base = classWithProperties({ + get x() { return 'boolean' as const }, + y: 'string', +}, class Base { +}); + +class MyClass extends Base { + get x() { + return false; + } + get y() { + return 'hi' + } +} + +const mine = new MyClass(); +const value = mine.x; + diff --git a/tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty9.ts b/tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty9.ts new file mode 100644 index 0000000000000..9dfa1a833b0c8 --- /dev/null +++ b/tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty9.ts @@ -0,0 +1,49 @@ +// @strict: true +// @target: es2017 +// #41347, based on microsoft/rushstack + +// Mixin utilities +export type Constructor = new (...args: any[]) => T; +export type PropertiesOf = { [K in keyof T]: T[K] }; + +interface IApiItemConstructor extends Constructor, PropertiesOf {} + +// Base class +class ApiItem { + public get members(): ReadonlyArray { + return []; + } +} + +// Normal subclass +class ApiEnumMember extends ApiItem { +} + +// Mixin base class +interface ApiItemContainerMixin extends ApiItem { + readonly members: ReadonlyArray; +} + +function ApiItemContainerMixin( + baseClass: TBaseClass +): TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) { + abstract class MixedClass extends baseClass implements ApiItemContainerMixin { + public constructor(...args: any[]) { + super(...args); + } + + public get members(): ReadonlyArray { + return []; + } + } + + return MixedClass; +} + +// Subclass inheriting from mixin +export class ApiEnum extends ApiItemContainerMixin(ApiItem) { + // This worked prior to TypeScript 4.0: + public get members(): ReadonlyArray { + return []; + } +}